Files
spring-cloud-static/Greenwich.M3/single/spring-cloud.html
2018-12-12 17:15:35 -05:00

14250 lines
2.6 MiB

<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Spring Cloud</title><link rel="stylesheet" type="text/css" href="css/manual-singlepage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.78.1"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div lang="en" class="book"><div class="titlepage"><div><div><h1 class="title"><a name="d0e3"></a>Spring Cloud</h1></div></div><hr></div><div class="toc"><p><b>Table of Contents</b></p><dl class="toc"><dt><span class="preface"><a href="#d0e9"></a></span></dt><dt><span class="chapter"><a href="#_features">1. Features</a></span></dt><dt><span class="part"><a href="#_cloud_native_applications">I. Cloud Native Applications</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_spring_cloud_context_application_context_services">2. Spring Cloud Context: Application Context Services</a></span></dt><dd><dl><dt><span class="section"><a href="#_the_bootstrap_application_context">2.1. The Bootstrap Application Context</a></span></dt><dt><span class="section"><a href="#_application_context_hierarchies">2.2. Application Context Hierarchies</a></span></dt><dt><span class="section"><a href="#customizing-bootstrap-properties">2.3. Changing the Location of Bootstrap Properties</a></span></dt><dt><span class="section"><a href="#overriding-bootstrap-properties">2.4. Overriding the Values of Remote Properties</a></span></dt><dt><span class="section"><a href="#_customizing_the_bootstrap_configuration">2.5. Customizing the Bootstrap Configuration</a></span></dt><dt><span class="section"><a href="#customizing-bootstrap-property-sources">2.6. Customizing the Bootstrap Property Sources</a></span></dt><dt><span class="section"><a href="#_logging_configuration">2.7. Logging Configuration</a></span></dt><dt><span class="section"><a href="#_environment_changes">2.8. Environment Changes</a></span></dt><dt><span class="section"><a href="#refresh-scope">2.9. Refresh Scope</a></span></dt><dt><span class="section"><a href="#_encryption_and_decryption">2.10. Encryption and Decryption</a></span></dt><dt><span class="section"><a href="#_endpoints">2.11. Endpoints</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_spring_cloud_commons_common_abstractions">3. Spring Cloud Commons: Common Abstractions</a></span></dt><dd><dl><dt><span class="section"><a href="#__enablediscoveryclient">3.1. @EnableDiscoveryClient</a></span></dt><dd><dl><dt><span class="section"><a href="#_health_indicator">3.1.1. Health Indicator</a></span></dt><dt><span class="section"><a href="#_ordering_literal_discoveryclient_literal_instances">3.1.2. Ordering <code class="literal">DiscoveryClient</code> instances</a></span></dt></dl></dd><dt><span class="section"><a href="#_serviceregistry">3.2. ServiceRegistry</a></span></dt><dd><dl><dt><span class="section"><a href="#_serviceregistry_auto_registration">3.2.1. ServiceRegistry Auto-Registration</a></span></dt><dd><dl><dt><span class="section"><a href="#_serviceregistry_auto_registration_events">ServiceRegistry Auto-Registration Events</a></span></dt></dl></dd><dt><span class="section"><a href="#_service_registry_actuator_endpoint">3.2.2. Service Registry Actuator Endpoint</a></span></dt></dl></dd><dt><span class="section"><a href="#_spring_resttemplate_as_a_load_balancer_client">3.3. Spring RestTemplate as a Load Balancer Client</a></span></dt><dt><span class="section"><a href="#_spring_webclient_as_a_load_balancer_client">3.4. Spring WebClient as a Load Balancer Client</a></span></dt><dd><dl><dt><span class="section"><a href="#_retrying_failed_requests">3.4.1. Retrying Failed Requests</a></span></dt></dl></dd><dt><span class="section"><a href="#_multiple_resttemplate_objects">3.5. Multiple RestTemplate objects</a></span></dt><dt><span class="section"><a href="#loadbalanced-webclient">3.6. Spring WebFlux WebClient as a Load Balancer Client</a></span></dt><dt><span class="section"><a href="#ignore-network-interfaces">3.7. Ignore Network Interfaces</a></span></dt><dt><span class="section"><a href="#http-clients">3.8. HTTP Client Factories</a></span></dt><dt><span class="section"><a href="#enabled-features">3.9. Enabled Features</a></span></dt><dd><dl><dt><span class="section"><a href="#_feature_types">3.9.1. Feature types</a></span></dt><dt><span class="section"><a href="#_declaring_features">3.9.2. Declaring features</a></span></dt></dl></dd><dt><span class="section"><a href="#_spring_cloud_compatibility_verification">3.10. Spring Cloud Compatibility Verification</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="#_spring_cloud_config">II. Spring Cloud Config</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_quick_start">4. Quick Start</a></span></dt><dd><dl><dt><span class="section"><a href="#_client_side_usage">4.1. Client Side Usage</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_spring_cloud_config_server">5. Spring Cloud Config Server</a></span></dt><dd><dl><dt><span class="section"><a href="#_environment_repository">5.1. Environment Repository</a></span></dt><dd><dl><dt><span class="section"><a href="#_git_backend">5.1.1. Git Backend</a></span></dt><dd><dl><dt><span class="section"><a href="#_skipping_ssl_certificate_validation">Skipping SSL Certificate Validation</a></span></dt><dt><span class="section"><a href="#_setting_http_connection_timeout">Setting HTTP Connection Timeout</a></span></dt><dt><span class="section"><a href="#_placeholders_in_git_uri">Placeholders in Git URI</a></span></dt><dt><span class="section"><a href="#_pattern_matching_and_multiple_repositories">Pattern Matching and Multiple Repositories</a></span></dt><dt><span class="section"><a href="#_authentication">Authentication</a></span></dt><dt><span class="section"><a href="#_authentication_with_aws_codecommit">Authentication with AWS CodeCommit</a></span></dt><dt><span class="section"><a href="#_git_ssh_configuration_using_properties">Git SSH configuration using properties</a></span></dt><dt><span class="section"><a href="#_placeholders_in_git_search_paths">Placeholders in Git Search Paths</a></span></dt><dt><span class="section"><a href="#_force_pull_in_git_repositories">Force pull in Git Repositories</a></span></dt><dt><span class="section"><a href="#_deleting_untracked_branches_in_git_repositories">Deleting untracked branches in Git Repositories</a></span></dt><dt><span class="section"><a href="#_git_refresh_rate">Git Refresh Rate</a></span></dt></dl></dd><dt><span class="section"><a href="#_version_control_backend_filesystem_use">5.1.2. Version Control Backend Filesystem Use</a></span></dt><dt><span class="section"><a href="#_file_system_backend">5.1.3. File System Backend</a></span></dt><dt><span class="section"><a href="#vault-backend">5.1.4. Vault Backend</a></span></dt><dd><dl><dt><span class="section"><a href="#_multiple_properties_sources">Multiple Properties Sources</a></span></dt></dl></dd><dt><span class="section"><a href="#_accessing_backends_through_a_proxy">5.1.5. Accessing Backends Through a Proxy</a></span></dt><dt><span class="section"><a href="#_sharing_configuration_with_all_applications">5.1.6. Sharing Configuration With All Applications</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-config-server-file-based-repositories">File Based Repositories</a></span></dt><dt><span class="section"><a href="#spring-cloud-config-server-vault-server">Vault Server</a></span></dt></dl></dd><dt><span class="section"><a href="#_jdbc_backend">5.1.7. JDBC Backend</a></span></dt><dt><span class="section"><a href="#composite-environment-repositories">5.1.8. Composite Environment Repositories</a></span></dt><dd><dl><dt><span class="section"><a href="#_custom_composite_environment_repositories">Custom Composite Environment Repositories</a></span></dt></dl></dd><dt><span class="section"><a href="#_property_overrides">5.1.9. Property Overrides</a></span></dt></dl></dd><dt><span class="section"><a href="#_health_indicator_2">5.2. Health Indicator</a></span></dt><dt><span class="section"><a href="#_security">5.3. Security</a></span></dt><dt><span class="section"><a href="#_encryption_and_decryption_2">5.4. Encryption and Decryption</a></span></dt><dt><span class="section"><a href="#_key_management">5.5. Key Management</a></span></dt><dt><span class="section"><a href="#_creating_a_key_store_for_testing">5.6. Creating a Key Store for Testing</a></span></dt><dt><span class="section"><a href="#_using_multiple_keys_and_key_rotation">5.7. Using Multiple Keys and Key Rotation</a></span></dt><dt><span class="section"><a href="#_serving_encrypted_properties">5.8. Serving Encrypted Properties</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_serving_alternative_formats">6. Serving Alternative Formats</a></span></dt><dt><span class="chapter"><a href="#_serving_plain_text">7. Serving Plain Text</a></span></dt><dt><span class="chapter"><a href="#_embedding_the_config_server">8. Embedding the Config Server</a></span></dt><dt><span class="chapter"><a href="#_push_notifications_and_spring_cloud_bus">9. Push Notifications and Spring Cloud Bus</a></span></dt><dt><span class="chapter"><a href="#_spring_cloud_config_client">10. Spring Cloud Config Client</a></span></dt><dd><dl><dt><span class="section"><a href="#config-first-bootstrap">10.1. Config First Bootstrap</a></span></dt><dt><span class="section"><a href="#discovery-first-bootstrap">10.2. Discovery First Bootstrap</a></span></dt><dt><span class="section"><a href="#config-client-fail-fast">10.3. Config Client Fail Fast</a></span></dt><dt><span class="section"><a href="#config-client-retry">10.4. Config Client Retry</a></span></dt><dt><span class="section"><a href="#_locating_remote_configuration_resources">10.5. Locating Remote Configuration Resources</a></span></dt><dt><span class="section"><a href="#_specifying_multiple_urls_for_the_config_server">10.6. Specifying Multiple Urls for the Config Server</a></span></dt><dt><span class="section"><a href="#_configuring_read_timeouts">10.7. Configuring Read Timeouts</a></span></dt><dt><span class="section"><a href="#_security_2">10.8. Security</a></span></dt><dd><dl><dt><span class="section"><a href="#_health_indicator_3">10.8.1. Health Indicator</a></span></dt><dt><span class="section"><a href="#custom-rest-template">10.8.2. Providing A Custom RestTemplate</a></span></dt><dt><span class="section"><a href="#_vault">10.8.3. Vault</a></span></dt></dl></dd><dt><span class="section"><a href="#_nested_keys_in_vault">10.9. Nested Keys In Vault</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="#_spring_cloud_netflix">III. Spring Cloud Netflix</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_service_discovery_eureka_clients">11. Service Discovery: Eureka Clients</a></span></dt><dd><dl><dt><span class="section"><a href="#netflix-eureka-client-starter">11.1. How to Include Eureka Client</a></span></dt><dt><span class="section"><a href="#_registering_with_eureka">11.2. Registering with Eureka</a></span></dt><dt><span class="section"><a href="#_authenticating_with_the_eureka_server">11.3. Authenticating with the Eureka Server</a></span></dt><dt><span class="section"><a href="#_status_page_and_health_indicator">11.4. Status Page and Health Indicator</a></span></dt><dt><span class="section"><a href="#_registering_a_secure_application">11.5. Registering a Secure Application</a></span></dt><dt><span class="section"><a href="#_eureka_s_health_checks">11.6. Eureka&#8217;s Health Checks</a></span></dt><dt><span class="section"><a href="#_eureka_metadata_for_instances_and_clients">11.7. Eureka Metadata for Instances and Clients</a></span></dt><dd><dl><dt><span class="section"><a href="#_using_eureka_on_cloud_foundry">11.7.1. Using Eureka on Cloud Foundry</a></span></dt><dt><span class="section"><a href="#_using_eureka_on_aws">11.7.2. Using Eureka on AWS</a></span></dt><dt><span class="section"><a href="#_changing_the_eureka_instance_id">11.7.3. Changing the Eureka Instance ID</a></span></dt></dl></dd><dt><span class="section"><a href="#_using_the_eurekaclient">11.8. Using the EurekaClient</a></span></dt><dd><dl><dt><span class="section"><a href="#_eurekaclient_without_jersey">11.8.1. EurekaClient without Jersey</a></span></dt></dl></dd><dt><span class="section"><a href="#_alternatives_to_the_native_netflix_eurekaclient">11.9. Alternatives to the Native Netflix EurekaClient</a></span></dt><dt><span class="section"><a href="#_why_is_it_so_slow_to_register_a_service">11.10. Why Is It so Slow to Register a Service?</a></span></dt><dt><span class="section"><a href="#_zones">11.11. Zones</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-eureka-server">12. Service Discovery: Eureka Server</a></span></dt><dd><dl><dt><span class="section"><a href="#netflix-eureka-server-starter">12.1. How to Include Eureka Server</a></span></dt><dt><span class="section"><a href="#spring-cloud-running-eureka-server">12.2. How to Run a Eureka Server</a></span></dt><dt><span class="section"><a href="#spring-cloud-eureka-server-zones-and-regions">12.3. High Availability, Zones and Regions</a></span></dt><dt><span class="section"><a href="#spring-cloud-eureka-server-standalone-mode">12.4. Standalone Mode</a></span></dt><dt><span class="section"><a href="#spring-cloud-eureka-server-peer-awareness">12.5. Peer Awareness</a></span></dt><dt><span class="section"><a href="#spring-cloud-eureka-server-prefer-ip-address">12.6. When to Prefer IP Address</a></span></dt><dt><span class="section"><a href="#_securing_the_eureka_server">12.7. Securing The Eureka Server</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_circuit_breaker_hystrix_clients">13. Circuit Breaker: Hystrix Clients</a></span></dt><dd><dl><dt><span class="section"><a href="#_how_to_include_hystrix">13.1. How to Include Hystrix</a></span></dt><dt><span class="section"><a href="#netflix-hystrix-starter">13.2. Propagating the Security Context or Using Spring Scopes</a></span></dt><dt><span class="section"><a href="#_health_indicator_4">13.3. Health Indicator</a></span></dt><dt><span class="section"><a href="#_hystrix_metrics_stream">13.4. Hystrix Metrics Stream</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_circuit_breaker_hystrix_dashboard">14. Circuit Breaker: Hystrix Dashboard</a></span></dt><dt><span class="chapter"><a href="#_hystrix_timeouts_and_ribbon_clients">15. Hystrix Timeouts And Ribbon Clients</a></span></dt><dd><dl><dt><span class="section"><a href="#netflix-hystrix-dashboard-starter">15.1. How to Include the Hystrix Dashboard</a></span></dt><dt><span class="section"><a href="#_turbine">15.2. Turbine</a></span></dt><dd><dl><dt><span class="section"><a href="#_clusters_endpoint">15.2.1. Clusters Endpoint</a></span></dt></dl></dd><dt><span class="section"><a href="#_turbine_stream">15.3. Turbine Stream</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-ribbon">16. Client Side Load Balancer: Ribbon</a></span></dt><dd><dl><dt><span class="section"><a href="#netflix-ribbon-starter">16.1. How to Include Ribbon</a></span></dt><dt><span class="section"><a href="#_customizing_the_ribbon_client">16.2. Customizing the Ribbon Client</a></span></dt><dt><span class="section"><a href="#_customizing_the_default_for_all_ribbon_clients">16.3. Customizing the Default for All Ribbon Clients</a></span></dt><dt><span class="section"><a href="#_customizing_the_ribbon_client_by_setting_properties">16.4. Customizing the Ribbon Client by Setting Properties</a></span></dt><dt><span class="section"><a href="#_using_ribbon_with_eureka">16.5. Using Ribbon with Eureka</a></span></dt><dt><span class="section"><a href="#spring-cloud-ribbon-without-eureka">16.6. Example: How to Use Ribbon Without Eureka</a></span></dt><dt><span class="section"><a href="#_example_disable_eureka_use_in_ribbon">16.7. Example: Disable Eureka Use in Ribbon</a></span></dt><dt><span class="section"><a href="#_using_the_ribbon_api_directly">16.8. Using the Ribbon API Directly</a></span></dt><dt><span class="section"><a href="#ribbon-child-context-eager-load">16.9. Caching of Ribbon Configuration</a></span></dt><dt><span class="section"><a href="#how-to-configure-hystrix-thread-pools">16.10. How to Configure Hystrix Thread Pools</a></span></dt><dt><span class="section"><a href="#how-to-provdie-a-key-to-ribbon">16.11. How to Provide a Key to Ribbon&#8217;s <code class="literal">IRule</code></a></span></dt></dl></dd><dt><span class="chapter"><a href="#_external_configuration_archaius">17. External Configuration: Archaius</a></span></dt><dt><span class="chapter"><a href="#_router_and_filter_zuul">18. Router and Filter: Zuul</a></span></dt><dd><dl><dt><span class="section"><a href="#netflix-zuul-starter">18.1. How to Include Zuul</a></span></dt><dt><span class="section"><a href="#netflix-zuul-reverse-proxy">18.2. Embedded Zuul Reverse Proxy</a></span></dt><dt><span class="section"><a href="#_zuul_http_client">18.3. Zuul Http Client</a></span></dt><dt><span class="section"><a href="#_cookies_and_sensitive_headers">18.4. Cookies and Sensitive Headers</a></span></dt><dt><span class="section"><a href="#_ignored_headers">18.5. Ignored Headers</a></span></dt><dt><span class="section"><a href="#_management_endpoints">18.6. Management Endpoints</a></span></dt><dd><dl><dt><span class="section"><a href="#_routes_endpoint">18.6.1. Routes Endpoint</a></span></dt><dt><span class="section"><a href="#_filters_endpoint">18.6.2. Filters Endpoint</a></span></dt></dl></dd><dt><span class="section"><a href="#_strangulation_patterns_and_local_forwards">18.7. Strangulation Patterns and Local Forwards</a></span></dt><dt><span class="section"><a href="#_uploading_files_through_zuul">18.8. Uploading Files through Zuul</a></span></dt><dt><span class="section"><a href="#_query_string_encoding">18.9. Query String Encoding</a></span></dt><dt><span class="section"><a href="#_plain_embedded_zuul">18.10. Plain Embedded Zuul</a></span></dt><dt><span class="section"><a href="#_disable_zuul_filters">18.11. Disable Zuul Filters</a></span></dt><dt><span class="section"><a href="#hystrix-fallbacks-for-routes">18.12. Providing Hystrix Fallbacks For Routes</a></span></dt><dt><span class="section"><a href="#_zuul_timeouts">18.13. Zuul Timeouts</a></span></dt><dt><span class="section"><a href="#zuul-redirect-location-rewrite">18.14. Rewriting the <code class="literal">Location</code> header</a></span></dt><dt><span class="section"><a href="#_enabling_cross_origin_requests">18.15. Enabling Cross Origin Requests</a></span></dt><dt><span class="section"><a href="#_metrics">18.16. Metrics</a></span></dt><dt><span class="section"><a href="#zuul-developer-guide">18.17. Zuul Developer Guide</a></span></dt><dd><dl><dt><span class="section"><a href="#_the_zuul_servlet">18.17.1. The Zuul Servlet</a></span></dt><dt><span class="section"><a href="#_zuul_requestcontext">18.17.2. Zuul RequestContext</a></span></dt><dt><span class="section"><a href="#__literal_enablezuulproxy_literal_vs_literal_enablezuulserver_literal">18.17.3. <code class="literal">@EnableZuulProxy</code> vs. <code class="literal">@EnableZuulServer</code></a></span></dt><dt><span class="section"><a href="#zuul-developer-guide-enable-filters">18.17.4. <code class="literal">@EnableZuulServer</code> Filters</a></span></dt><dt><span class="section"><a href="#__literal_enablezuulproxy_literal_filters">18.17.5. <code class="literal">@EnableZuulProxy</code> Filters</a></span></dt><dt><span class="section"><a href="#_custom_zuul_filter_examples">18.17.6. Custom Zuul Filter Examples</a></span></dt><dd><dl><dt><span class="section"><a href="#zuul-developer-guide-sample-pre-filter">How to Write a Pre Filter</a></span></dt><dt><span class="section"><a href="#zuul-developer-guide-sample-route-filter">How to Write a Route Filter</a></span></dt><dt><span class="section"><a href="#zuul-developer-guide-sample-post-filter">How to Write a Post Filter</a></span></dt></dl></dd><dt><span class="section"><a href="#_how_zuul_errors_work">18.17.7. How Zuul Errors Work</a></span></dt><dt><span class="section"><a href="#_zuul_eager_application_context_loading">18.17.8. Zuul Eager Application Context Loading</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_polyglot_support_with_sidecar">19. Polyglot support with Sidecar</a></span></dt><dt><span class="chapter"><a href="#retrying-failed-requests">20. Retrying Failed Requests</a></span></dt><dd><dl><dt><span class="section"><a href="#_backoff_policies">20.1. BackOff Policies</a></span></dt><dt><span class="section"><a href="#_configuration">20.2. Configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#_zuul">20.2.1. Zuul</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_http_clients">21. HTTP Clients</a></span></dt></dl></dd><dt><span class="part"><a href="#_spring_cloud_openfeign">IV. Spring Cloud OpenFeign</a></span></dt><dd><dl><dt><span class="chapter"><a href="#spring-cloud-feign">22. Declarative REST Client: Feign</a></span></dt><dd><dl><dt><span class="section"><a href="#netflix-feign-starter">22.1. How to Include Feign</a></span></dt><dt><span class="section"><a href="#spring-cloud-feign-overriding-defaults">22.2. Overriding Feign Defaults</a></span></dt><dt><span class="section"><a href="#_creating_feign_clients_manually">22.3. Creating Feign Clients Manually</a></span></dt><dt><span class="section"><a href="#spring-cloud-feign-hystrix">22.4. Feign Hystrix Support</a></span></dt><dt><span class="section"><a href="#spring-cloud-feign-hystrix-fallback">22.5. Feign Hystrix Fallbacks</a></span></dt><dt><span class="section"><a href="#_feign_and_literal_primary_literal">22.6. Feign and <code class="literal">@Primary</code></a></span></dt><dt><span class="section"><a href="#spring-cloud-feign-inheritance">22.7. Feign Inheritance Support</a></span></dt><dt><span class="section"><a href="#_feign_request_response_compression">22.8. Feign request/response compression</a></span></dt><dt><span class="section"><a href="#_feign_logging">22.9. Feign logging</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="#_spring_cloud_stream">V. Spring Cloud Stream</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_a_brief_history_of_spring_s_data_integration_journey">23. A Brief History of Spring&#8217;s Data Integration Journey</a></span></dt><dt><span class="chapter"><a href="#_quick_start_2">24. Quick Start</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-preface-creating-sample-application">24.1. Creating a Sample Application by Using Spring Initializr</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-preface-importing-project">24.2. Importing the Project into Your IDE</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-preface-adding-message-handler">24.3. Adding a Message Handler, Building, and Running</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_what_s_new_in_2_0">25. What&#8217;s New in 2.0?</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-preface-new-features">25.1. New Features and Components</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-preface-notable-enhancements">25.2. Notable Enhancements</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-preface-actuator-web-dependencies">25.2.1. Both Actuator and Web Dependencies Are Now Optional</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-preface-content-type-negotiation-improvements">25.2.2. Content-type Negotiation Improvements</a></span></dt></dl></dd><dt><span class="section"><a href="#spring-cloud-stream-preface-notable-deprecations">25.3. Notable Deprecations</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-preface-deprecation-java-serialization">25.3.1. Java Serialization (Java Native and Kryo)</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-preface-deprecation-classes-methods">25.3.2. Deprecated Classes and Methods</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#spring-cloud-stream-overview-introducing">26. Introducing Spring Cloud Stream</a></span></dt><dt><span class="chapter"><a href="#_main_concepts">27. Main Concepts</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-overview-application-model">27.1. Application Model</a></span></dt><dd><dl><dt><span class="section"><a href="#_fat_jar">27.1.1. Fat JAR</a></span></dt></dl></dd><dt><span class="section"><a href="#spring-cloud-stream-overview-binder-abstraction">27.2. The Binder Abstraction</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-persistent-publish-subscribe-support">27.3. Persistent Publish-Subscribe Support</a></span></dt><dt><span class="section"><a href="#consumer-groups">27.4. Consumer Groups</a></span></dt><dt><span class="section"><a href="#consumer-types">27.5. Consumer Types</a></span></dt><dd><dl><dt><span class="section"><a href="#durability">27.5.1. Durability</a></span></dt></dl></dd><dt><span class="section"><a href="#partitioning">27.6. Partitioning Support</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_programming_model">28. Programming Model</a></span></dt><dd><dl><dt><span class="section"><a href="#_destination_binders">28.1. Destination Binders</a></span></dt><dt><span class="section"><a href="#_destination_bindings">28.2. Destination Bindings</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-producing-consuming-messages">28.3. Producing and Consuming Messages</a></span></dt><dd><dl><dt><span class="section"><a href="#_spring_integration_support">28.3.1. Spring Integration Support</a></span></dt><dt><span class="section"><a href="#_using_streamlistener_annotation">28.3.2. Using @StreamListener Annotation</a></span></dt><dt><span class="section"><a href="#_using_streamlistener_for_content_based_routing">28.3.3. Using @StreamListener for Content-based routing</a></span></dt><dt><span class="section"><a href="#_spring_cloud_function">28.3.4. Spring Cloud Function support</a></span></dt><dd><dl><dt><span class="section"><a href="#_functional_composition">Functional Composition</a></span></dt></dl></dd><dt><span class="section"><a href="#spring-cloud-streams-overview-using-polled-consumers">28.3.5. Using Polled Consumers</a></span></dt><dd><dl><dt><span class="section"><a href="#_overview">Overview</a></span></dt><dt><span class="section"><a href="#polled-errors">Handling Errors</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#spring-cloud-stream-overview-error-handling">28.4. Error Handling</a></span></dt><dd><dl><dt><span class="section"><a href="#_application_error_handling">28.4.1. Application Error Handling</a></span></dt><dt><span class="section"><a href="#_system_error_handling">28.4.2. System Error Handling</a></span></dt><dd><dl><dt><span class="section"><a href="#_drop_failed_messages">Drop Failed Messages</a></span></dt><dt><span class="section"><a href="#_dlq_dead_letter_queue">DLQ - Dead Letter Queue</a></span></dt><dt><span class="section"><a href="#_re_queue_failed_messages">Re-queue Failed Messages</a></span></dt></dl></dd><dt><span class="section"><a href="#_retry_template">28.4.3. Retry Template</a></span></dt></dl></dd><dt><span class="section"><a href="#spring-cloud-stream-overview-reactive-programming-support">28.5. Reactive Programming Support</a></span></dt><dd><dl><dt><span class="section"><a href="#_reactor_based_handlers">28.5.1. Reactor-based Handlers</a></span></dt><dt><span class="section"><a href="#_reactive_sources">28.5.2. Reactive Sources</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#spring-cloud-stream-overview-binders">29. Binders</a></span></dt><dd><dl><dt><span class="section"><a href="#_producers_and_consumers">29.1. Producers and Consumers</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-binder-api">29.2. Binder SPI</a></span></dt><dt><span class="section"><a href="#_binder_detection">29.3. Binder Detection</a></span></dt><dd><dl><dt><span class="section"><a href="#_classpath_detection">29.3.1. Classpath Detection</a></span></dt></dl></dd><dt><span class="section"><a href="#multiple-binders">29.4. Multiple Binders on the Classpath</a></span></dt><dt><span class="section"><a href="#multiple-systems">29.5. Connecting to Multiple Systems</a></span></dt><dt><span class="section"><a href="#_binding_visualization_and_control">29.6. Binding visualization and control</a></span></dt><dt><span class="section"><a href="#_binder_configuration_properties">29.7. Binder Configuration Properties</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_configuration_options">30. Configuration Options</a></span></dt><dd><dl><dt><span class="section"><a href="#_binding_service_properties">30.1. Binding Service Properties</a></span></dt><dt><span class="section"><a href="#binding-properties">30.2. Binding Properties</a></span></dt><dd><dl><dt><span class="section"><a href="#_common_binding_properties">30.2.1. Common Binding Properties</a></span></dt><dt><span class="section"><a href="#_consumer_properties">30.2.2. Consumer Properties</a></span></dt><dt><span class="section"><a href="#_producer_properties">30.2.3. Producer Properties</a></span></dt></dl></dd><dt><span class="section"><a href="#dynamicdestination">30.3. Using Dynamically Bound Destinations</a></span></dt></dl></dd><dt><span class="chapter"><a href="#content-type-management">31. Content Type Negotiation</a></span></dt><dd><dl><dt><span class="section"><a href="#_mechanics">31.1. Mechanics</a></span></dt><dd><dl><dt><span class="section"><a href="#_content_type_versus_argument_type">31.1.1. Content Type versus Argument Type</a></span></dt><dt><span class="section"><a href="#_message_converters">31.1.2. Message Converters</a></span></dt></dl></dd><dt><span class="section"><a href="#_provided_messageconverters">31.2. Provided MessageConverters</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-user-defined-message-converters">31.3. User-defined Message Converters</a></span></dt></dl></dd><dt><span class="chapter"><a href="#schema-evolution">32. Schema Evolution Support</a></span></dt><dd><dl><dt><span class="section"><a href="#_schema_registry_client">32.1. Schema Registry Client</a></span></dt><dd><dl><dt><span class="section"><a href="#_schema_registry_client_properties">32.1.1. Schema Registry Client Properties</a></span></dt></dl></dd><dt><span class="section"><a href="#_avro_schema_registry_client_message_converters">32.2. Avro Schema Registry Client Message Converters</a></span></dt><dd><dl><dt><span class="section"><a href="#_avro_schema_registry_message_converter_properties">32.2.1. Avro Schema Registry Message Converter Properties</a></span></dt></dl></dd><dt><span class="section"><a href="#_apache_avro_message_converters">32.3. Apache Avro Message Converters</a></span></dt><dt><span class="section"><a href="#_converters_with_schema_support">32.4. Converters with Schema Support</a></span></dt><dt><span class="section"><a href="#_schema_registry_server">32.5. Schema Registry Server</a></span></dt><dd><dl><dt><span class="section"><a href="#_schema_registry_server_api">32.5.1. Schema Registry Server API</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-overview-registering-new-schema">Registering a New Schema</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-retrieve-schema-subject-format-version">Retrieving an Existing Schema by Subject, Format, and Version</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-retrieve-schema-subject-format">Retrieving an Existing Schema by Subject and Format</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-retrieve-schema-id">Retrieving an Existing Schema by ID</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-deleting-schema-subject-format-version">Deleting a Schema by Subject, Format, and Version</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-deleting-schema-id">Deleting a Schema by ID</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-deleting-schema-subject">Deleting a Schema by Subject</a></span></dt></dl></dd><dt><span class="section"><a href="#_using_confluent_s_schema_registry">32.5.2. Using Confluent&#8217;s Schema Registry</a></span></dt></dl></dd><dt><span class="section"><a href="#_schema_registration_and_resolution">32.6. Schema Registration and Resolution</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-overview-schema-registration-process">32.6.1. Schema Registration Process (Serialization)</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-schema-resolution-process">32.6.2. Schema Resolution Process (Deserialization)</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_inter_application_communication">33. Inter-Application Communication</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-overview-connecting-multiple-application-instances">33.1. Connecting Multiple Application Instances</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-instance-index-instance-count">33.2. Instance Index and Instance Count</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-partitioning">33.3. Partitioning</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-stream-overview-configuring-output-bindings-partitioning">33.3.1. Configuring Output Bindings for Partitioning</a></span></dt><dt><span class="section"><a href="#spring-cloud-stream-overview-configuring-input-bindings-partitioning">33.3.2. Configuring Input Bindings for Partitioning</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_testing">34. Testing</a></span></dt><dd><dl><dt><span class="section"><a href="#_disabling_the_test_binder_autoconfiguration">34.1. Disabling the Test Binder Autoconfiguration</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_health_indicator_5">35. Health Indicator</a></span></dt><dt><span class="chapter"><a href="#spring-cloud-stream-overview-metrics-emitter">36. Metrics Emitter</a></span></dt><dt><span class="chapter"><a href="#_samples">37. Samples</a></span></dt><dd><dl><dt><span class="section"><a href="#_deploying_stream_applications_on_cloudfoundry">37.1. Deploying Stream Applications on CloudFoundry</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="#_binder_implementations">VI. Binder Implementations</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_apache_kafka_binder">38. Apache Kafka Binder</a></span></dt><dd><dl><dt><span class="section"><a href="#_usage">38.1. Usage</a></span></dt><dt><span class="section"><a href="#_apache_kafka_binder_overview">38.2. Apache Kafka Binder Overview</a></span></dt><dt><span class="section"><a href="#_configuration_options_2">38.3. Configuration Options</a></span></dt><dd><dl><dt><span class="section"><a href="#_kafka_binder_properties">38.3.1. Kafka Binder Properties</a></span></dt><dt><span class="section"><a href="#kafka-consumer-properties">38.3.2. Kafka Consumer Properties</a></span></dt><dt><span class="section"><a href="#kafka-producer-properties">38.3.3. Kafka Producer Properties</a></span></dt><dt><span class="section"><a href="#_usage_examples">38.3.4. Usage examples</a></span></dt><dd><dl><dt><span class="section"><a href="#_example_setting_literal_autocommitoffset_literal_to_literal_false_literal_and_relying_on_manual_acking">Example: Setting <code class="literal">autoCommitOffset</code> to <code class="literal">false</code> and Relying on Manual Acking</a></span></dt><dt><span class="section"><a href="#_example_security_configuration">Example: Security Configuration</a></span></dt><dt><span class="section"><a href="#pause-resume">Example: Pausing and Resuming the Consumer</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#kafka-error-channels">38.4. Error Channels</a></span></dt><dt><span class="section"><a href="#kafka-metrics">38.5. Kafka Metrics</a></span></dt><dt><span class="section"><a href="#kafka-dlq-processing">38.6. Dead-Letter Topic Processing</a></span></dt><dt><span class="section"><a href="#_partitioning_with_the_kafka_binder">38.7. Partitioning with the Kafka Binder</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_apache_kafka_streams_binder">39. Apache Kafka Streams Binder</a></span></dt><dd><dl><dt><span class="section"><a href="#_usage_2">39.1. Usage</a></span></dt><dt><span class="section"><a href="#_kafka_streams_binder_overview">39.2. Kafka Streams Binder Overview</a></span></dt><dd><dl><dt><span class="section"><a href="#_streams_dsl">39.2.1. Streams DSL</a></span></dt></dl></dd><dt><span class="section"><a href="#_configuration_options_3">39.3. Configuration Options</a></span></dt><dd><dl><dt><span class="section"><a href="#_kafka_streams_properties">39.3.1. Kafka Streams Properties</a></span></dt><dt><span class="section"><a href="#_timewindow_properties">39.3.2. TimeWindow properties:</a></span></dt></dl></dd><dt><span class="section"><a href="#_multiple_input_bindings">39.4. Multiple Input Bindings</a></span></dt><dd><dl><dt><span class="section"><a href="#_multiple_input_bindings_as_a_sink">39.4.1. Multiple Input Bindings as a Sink</a></span></dt><dt><span class="section"><a href="#_multiple_input_bindings_as_a_processor">39.4.2. Multiple Input Bindings as a Processor</a></span></dt></dl></dd><dt><span class="section"><a href="#_multiple_output_bindings_aka_branching">39.5. Multiple Output Bindings (aka Branching)</a></span></dt><dt><span class="section"><a href="#_message_conversion">39.6. Message Conversion</a></span></dt><dd><dl><dt><span class="section"><a href="#_outbound_serialization">39.6.1. Outbound serialization</a></span></dt><dt><span class="section"><a href="#_inbound_deserialization">39.6.2. Inbound Deserialization</a></span></dt></dl></dd><dt><span class="section"><a href="#_error_handling">39.7. Error Handling</a></span></dt><dd><dl><dt><span class="section"><a href="#_handling_deserialization_exceptions">39.7.1. Handling Deserialization Exceptions</a></span></dt><dt><span class="section"><a href="#_handling_non_deserialization_exceptions">39.7.2. Handling Non-Deserialization Exceptions</a></span></dt></dl></dd><dt><span class="section"><a href="#_state_store">39.8. State Store</a></span></dt><dt><span class="section"><a href="#_interactive_queries">39.9. Interactive Queries</a></span></dt><dt><span class="section"><a href="#_accessing_the_underlying_kafkastreams_object">39.10. Accessing the underlying KafkaStreams object</a></span></dt><dt><span class="section"><a href="#_state_cleanup">39.11. State Cleanup</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_rabbitmq_binder">40. RabbitMQ Binder</a></span></dt><dd><dl><dt><span class="section"><a href="#_usage_3">40.1. Usage</a></span></dt><dt><span class="section"><a href="#_rabbitmq_binder_overview">40.2. RabbitMQ Binder Overview</a></span></dt><dt><span class="section"><a href="#_configuration_options_4">40.3. Configuration Options</a></span></dt><dd><dl><dt><span class="section"><a href="#rabbit-binder-properties">40.3.1. RabbitMQ Binder Properties</a></span></dt><dt><span class="section"><a href="#_rabbitmq_consumer_properties">40.3.2. RabbitMQ Consumer Properties</a></span></dt><dt><span class="section"><a href="#_advanced_listener_container_configuration">40.3.3. Advanced Listener Container Configuration</a></span></dt><dt><span class="section"><a href="#_rabbit_producer_properties">40.3.4. Rabbit Producer Properties</a></span></dt></dl></dd><dt><span class="section"><a href="#_retry_with_the_rabbitmq_binder">40.4. Retry With the RabbitMQ Binder</a></span></dt><dd><dl><dt><span class="section"><a href="#_putting_it_all_together">40.4.1. Putting it All Together</a></span></dt></dl></dd><dt><span class="section"><a href="#rabbit-error-channels">40.5. Error Channels</a></span></dt><dt><span class="section"><a href="#rabbit-dlq-processing">40.6. Dead-Letter Queue Processing</a></span></dt><dd><dl><dt><span class="section"><a href="#_non_partitioned_destinations">40.6.1. Non-Partitioned Destinations</a></span></dt><dt><span class="section"><a href="#_partitioned_destinations">40.6.2. Partitioned Destinations</a></span></dt><dd><dl><dt><span class="section"><a href="#__literal_republishtodlq_false_literal"><code class="literal">republishToDlq=false</code></a></span></dt><dt><span class="section"><a href="#__literal_republishtodlq_true_literal"><code class="literal">republishToDlq=true</code></a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_partitioning_with_the_rabbitmq_binder">40.7. Partitioning with the RabbitMQ Binder</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="#_spring_cloud_bus">VII. Spring Cloud Bus</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_quick_start_3">41. Quick Start</a></span></dt><dt><span class="chapter"><a href="#_bus_endpoints">42. Bus Endpoints</a></span></dt><dd><dl><dt><span class="section"><a href="#_bus_refresh_endpoint">42.1. Bus Refresh Endpoint</a></span></dt><dt><span class="section"><a href="#_bus_env_endpoint">42.2. Bus Env Endpoint</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_addressing_an_instance">43. Addressing an Instance</a></span></dt><dt><span class="chapter"><a href="#_addressing_all_instances_of_a_service">44. Addressing All Instances of a Service</a></span></dt><dt><span class="chapter"><a href="#_service_id_must_be_unique">45. Service ID Must Be Unique</a></span></dt><dt><span class="chapter"><a href="#_customizing_the_message_broker">46. Customizing the Message Broker</a></span></dt><dt><span class="chapter"><a href="#_tracing_bus_events">47. Tracing Bus Events</a></span></dt><dt><span class="chapter"><a href="#_broadcasting_your_own_events">48. Broadcasting Your Own Events</a></span></dt><dd><dl><dt><span class="section"><a href="#_registering_events_in_custom_packages">48.1. Registering events in custom packages</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="#_spring_cloud_sleuth">VIII. Spring Cloud Sleuth</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_introduction">49. Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#_terminology">49.1. Terminology</a></span></dt><dt><span class="section"><a href="#_purpose">49.2. Purpose</a></span></dt><dd><dl><dt><span class="section"><a href="#_distributed_tracing_with_zipkin">49.2.1. Distributed Tracing with Zipkin</a></span></dt><dt><span class="section"><a href="#_visualizing_errors">49.2.2. Visualizing errors</a></span></dt><dt><span class="section"><a href="#_distributed_tracing_with_brave">49.2.3. Distributed Tracing with Brave</a></span></dt><dt><span class="section"><a href="#_live_examples">49.2.4. Live examples</a></span></dt><dt><span class="section"><a href="#_log_correlation">49.2.5. Log correlation</a></span></dt><dd><dl><dt><span class="section"><a href="#_json_logback_with_logstash">JSON Logback with Logstash</a></span></dt></dl></dd><dt><span class="section"><a href="#_propagating_span_context">49.2.6. Propagating Span Context</a></span></dt><dd><dl><dt><span class="section"><a href="#_baggage_versus_span_tags">Baggage versus Span Tags</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#sleuth-adding-project">49.3. Adding Sleuth to the Project</a></span></dt><dd><dl><dt><span class="section"><a href="#_only_sleuth_log_correlation">49.3.1. Only Sleuth (log correlation)</a></span></dt><dt><span class="section"><a href="#_sleuth_with_zipkin_via_http">49.3.2. Sleuth with Zipkin via HTTP</a></span></dt><dt><span class="section"><a href="#_sleuth_with_zipkin_over_rabbitmq_or_kafka">49.3.3. Sleuth with Zipkin over RabbitMQ or Kafka</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_additional_resources">50. Additional Resources</a></span></dt><dt><span class="chapter"><a href="#_features_2">51. Features</a></span></dt><dd><dl><dt><span class="section"><a href="#_introduction_to_brave">51.1. Introduction to Brave</a></span></dt><dd><dl><dt><span class="section"><a href="#_tracing">51.1.1. Tracing</a></span></dt><dt><span class="section"><a href="#_local_tracing">51.1.2. Local Tracing</a></span></dt><dt><span class="section"><a href="#_customizing_spans">51.1.3. Customizing Spans</a></span></dt><dt><span class="section"><a href="#_implicitly_looking_up_the_current_span">51.1.4. Implicitly Looking up the Current Span</a></span></dt><dt><span class="section"><a href="#_rpc_tracing">51.1.5. RPC tracing</a></span></dt><dd><dl><dt><span class="section"><a href="#_one_way_tracing">One-Way tracing</a></span></dt></dl></dd></dl></dd></dl></dd><dt><span class="chapter"><a href="#_sampling">52. Sampling</a></span></dt><dd><dl><dt><span class="section"><a href="#_declarative_sampling">52.1. Declarative sampling</a></span></dt><dt><span class="section"><a href="#_custom_sampling">52.2. Custom sampling</a></span></dt><dt><span class="section"><a href="#_sampling_in_spring_cloud_sleuth">52.3. Sampling in Spring Cloud Sleuth</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_propagation">53. Propagation</a></span></dt><dd><dl><dt><span class="section"><a href="#_propagating_extra_fields">53.1. Propagating extra fields</a></span></dt><dd><dl><dt><span class="section"><a href="#prefixed-fields">53.1.1. Prefixed fields</a></span></dt><dt><span class="section"><a href="#_extracting_a_propagated_context">53.1.2. Extracting a Propagated Context</a></span></dt><dt><span class="section"><a href="#_sharing_span_ids_between_client_and_server">53.1.3. Sharing span IDs between Client and Server</a></span></dt><dt><span class="section"><a href="#_implementing_propagation">53.1.4. Implementing Propagation</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_current_tracing_component">54. Current Tracing Component</a></span></dt><dt><span class="chapter"><a href="#_current_span">55. Current Span</a></span></dt><dd><dl><dt><span class="section"><a href="#_setting_a_span_in_scope_manually">55.1. Setting a span in scope manually</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_instrumentation">56. Instrumentation</a></span></dt><dt><span class="chapter"><a href="#_span_lifecycle">57. Span lifecycle</a></span></dt><dd><dl><dt><span class="section"><a href="#creating-and-finishing-spans">57.1. Creating and finishing spans</a></span></dt><dt><span class="section"><a href="#continuing-spans">57.2. Continuing Spans</a></span></dt><dt><span class="section"><a href="#creating-spans-with-explicit-parent">57.3. Creating a Span with an explicit Parent</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_naming_spans">58. Naming spans</a></span></dt><dd><dl><dt><span class="section"><a href="#__literal_spanname_literal_annotation">58.1. <code class="literal">@SpanName</code> Annotation</a></span></dt><dt><span class="section"><a href="#__literal_tostring_literal_method">58.2. <code class="literal">toString()</code> method</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_managing_spans_with_annotations">59. Managing Spans with Annotations</a></span></dt><dd><dl><dt><span class="section"><a href="#_rationale">59.1. Rationale</a></span></dt><dt><span class="section"><a href="#_creating_new_spans">59.2. Creating New Spans</a></span></dt><dt><span class="section"><a href="#_continuing_spans">59.3. Continuing Spans</a></span></dt><dt><span class="section"><a href="#_advanced_tag_setting">59.4. Advanced Tag Setting</a></span></dt><dd><dl><dt><span class="section"><a href="#_custom_extractor">59.4.1. Custom extractor</a></span></dt><dt><span class="section"><a href="#_resolving_expressions_for_a_value">59.4.2. Resolving Expressions for a Value</a></span></dt><dt><span class="section"><a href="#_using_the_literal_tostring_literal_method">59.4.3. Using the <code class="literal">toString()</code> method</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_customizations">60. Customizations</a></span></dt><dd><dl><dt><span class="section"><a href="#_http">60.1. HTTP</a></span></dt><dt><span class="section"><a href="#__literal_tracingfilter_literal">60.2. <code class="literal">TracingFilter</code></a></span></dt><dt><span class="section"><a href="#_custom_service_name">60.3. Custom service name</a></span></dt><dt><span class="section"><a href="#_customization_of_reported_spans">60.4. Customization of Reported Spans</a></span></dt><dt><span class="section"><a href="#_host_locator">60.5. Host Locator</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_sending_spans_to_zipkin">61. Sending Spans to Zipkin</a></span></dt><dt><span class="chapter"><a href="#_zipkin_stream_span_consumer">62. Zipkin Stream Span Consumer</a></span></dt><dt><span class="chapter"><a href="#_integrations">63. Integrations</a></span></dt><dd><dl><dt><span class="section"><a href="#_opentracing">63.1. OpenTracing</a></span></dt><dt><span class="section"><a href="#_runnable_and_callable">63.2. Runnable and Callable</a></span></dt><dt><span class="section"><a href="#_hystrix">63.3. Hystrix</a></span></dt><dd><dl><dt><span class="section"><a href="#_custom_concurrency_strategy">63.3.1. Custom Concurrency Strategy</a></span></dt><dt><span class="section"><a href="#_manual_command_setting">63.3.2. Manual Command setting</a></span></dt></dl></dd><dt><span class="section"><a href="#_rxjava">63.4. RxJava</a></span></dt><dt><span class="section"><a href="#_http_integration">63.5. HTTP integration</a></span></dt><dd><dl><dt><span class="section"><a href="#_http_filter">63.5.1. HTTP Filter</a></span></dt><dt><span class="section"><a href="#_handlerinterceptor">63.5.2. HandlerInterceptor</a></span></dt><dt><span class="section"><a href="#_async_servlet_support">63.5.3. Async Servlet support</a></span></dt><dt><span class="section"><a href="#_webflux_support">63.5.4. WebFlux support</a></span></dt><dt><span class="section"><a href="#_dubbo_rpc_support">63.5.5. Dubbo RPC support</a></span></dt></dl></dd><dt><span class="section"><a href="#_http_client_integration">63.6. HTTP Client Integration</a></span></dt><dd><dl><dt><span class="section"><a href="#_synchronous_rest_template">63.6.1. Synchronous Rest Template</a></span></dt><dt><span class="section"><a href="#_asynchronous_rest_template">63.6.2. Asynchronous Rest Template</a></span></dt><dd><dl><dt><span class="section"><a href="#_multiple_asynchronous_rest_templates">Multiple Asynchronous Rest Templates</a></span></dt></dl></dd><dt><span class="section"><a href="#__literal_webclient_literal">63.6.3. <code class="literal">WebClient</code></a></span></dt><dt><span class="section"><a href="#_traverson">63.6.4. Traverson</a></span></dt><dt><span class="section"><a href="#_apache_literal_httpclientbuilder_literal_and_literal_httpasyncclientbuilder_literal">63.6.5. Apache <code class="literal">HttpClientBuilder</code> and <code class="literal">HttpAsyncClientBuilder</code></a></span></dt><dt><span class="section"><a href="#_netty_literal_httpclient_literal">63.6.6. Netty <code class="literal">HttpClient</code></a></span></dt><dt><span class="section"><a href="#__literal_userinforesttemplatecustomizer_literal">63.6.7. <code class="literal">UserInfoRestTemplateCustomizer</code></a></span></dt></dl></dd><dt><span class="section"><a href="#_feign">63.7. Feign</a></span></dt><dt><span class="section"><a href="#_asynchronous_communication">63.8. Asynchronous Communication</a></span></dt><dd><dl><dt><span class="section"><a href="#__literal_async_literal_annotated_methods">63.8.1. <code class="literal">@Async</code> Annotated methods</a></span></dt><dt><span class="section"><a href="#__literal_scheduled_literal_annotated_methods">63.8.2. <code class="literal">@Scheduled</code> Annotated Methods</a></span></dt><dt><span class="section"><a href="#_executor_executorservice_and_scheduledexecutorservice">63.8.3. Executor, ExecutorService, and ScheduledExecutorService</a></span></dt><dd><dl><dt><span class="section"><a href="#_customization_of_executors">Customization of Executors</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_messaging">63.9. Messaging</a></span></dt><dd><dl><dt><span class="section"><a href="#_spring_integration_and_spring_cloud_stream">63.9.1. Spring Integration and Spring Cloud Stream</a></span></dt><dt><span class="section"><a href="#_spring_rabbitmq">63.9.2. Spring RabbitMq</a></span></dt><dt><span class="section"><a href="#_spring_kafka">63.9.3. Spring Kafka</a></span></dt><dt><span class="section"><a href="#_spring_jms">63.9.4. Spring JMS</a></span></dt></dl></dd><dt><span class="section"><a href="#_zuul_2">63.10. Zuul</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_running_examples">64. Running examples</a></span></dt></dl></dd><dt><span class="part"><a href="#_spring_cloud_consul">IX. Spring Cloud Consul</a></span></dt><dd><dl><dt><span class="chapter"><a href="#spring-cloud-consul-install">65. Install Consul</a></span></dt><dt><span class="chapter"><a href="#spring-cloud-consul-agent">66. Consul Agent</a></span></dt><dt><span class="chapter"><a href="#spring-cloud-consul-discovery">67. Service Discovery with Consul</a></span></dt><dd><dl><dt><span class="section"><a href="#_how_to_activate">67.1. How to activate</a></span></dt><dt><span class="section"><a href="#_registering_with_consul">67.2. Registering with Consul</a></span></dt><dt><span class="section"><a href="#_http_health_check">67.3. HTTP Health Check</a></span></dt><dd><dl><dt><span class="section"><a href="#_metadata_and_consul_tags">67.3.1. Metadata and Consul tags</a></span></dt><dt><span class="section"><a href="#_making_the_consul_instance_id_unique">67.3.2. Making the Consul Instance ID Unique</a></span></dt><dt><span class="section"><a href="#_applying_headers_to_health_check_requests">67.3.3. Applying Headers to Health Check Requests</a></span></dt></dl></dd><dt><span class="section"><a href="#_looking_up_services">67.4. Looking up services</a></span></dt><dd><dl><dt><span class="section"><a href="#_using_ribbon">67.4.1. Using Ribbon</a></span></dt><dt><span class="section"><a href="#_using_the_discoveryclient">67.4.2. Using the DiscoveryClient</a></span></dt></dl></dd><dt><span class="section"><a href="#_consul_catalog_watch">67.5. Consul Catalog Watch</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-consul-config">68. Distributed Configuration with Consul</a></span></dt><dd><dl><dt><span class="section"><a href="#_how_to_activate_2">68.1. How to activate</a></span></dt><dt><span class="section"><a href="#_customizing">68.2. Customizing</a></span></dt><dt><span class="section"><a href="#spring-cloud-consul-config-watch">68.3. Config Watch</a></span></dt><dt><span class="section"><a href="#spring-cloud-consul-config-format">68.4. YAML or Properties with Config</a></span></dt><dt><span class="section"><a href="#spring-cloud-consul-config-git2consul">68.5. git2consul with Config</a></span></dt><dt><span class="section"><a href="#spring-cloud-consul-failfast">68.6. Fail Fast</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-consul-retry">69. Consul Retry</a></span></dt><dt><span class="chapter"><a href="#spring-cloud-consul-bus">70. Spring Cloud Bus with Consul</a></span></dt><dd><dl><dt><span class="section"><a href="#_how_to_activate_3">70.1. How to activate</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-consul-hystrix">71. Circuit Breaker with Hystrix</a></span></dt><dt><span class="chapter"><a href="#spring-cloud-consul-turbine">72. Hystrix metrics aggregation with Turbine and Consul</a></span></dt></dl></dd><dt><span class="part"><a href="#_spring_cloud_zookeeper">X. Spring Cloud Zookeeper</a></span></dt><dd><dl><dt><span class="chapter"><a href="#spring-cloud-zookeeper-install">73. Install Zookeeper</a></span></dt><dt><span class="chapter"><a href="#spring-cloud-zookeeper-discovery">74. Service Discovery with Zookeeper</a></span></dt><dd><dl><dt><span class="section"><a href="#_activating">74.1. Activating</a></span></dt><dt><span class="section"><a href="#_registering_with_zookeeper">74.2. Registering with Zookeeper</a></span></dt><dt><span class="section"><a href="#_using_the_discoveryclient_2">74.3. Using the DiscoveryClient</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-zookeeper-netflix">75. Using Spring Cloud Zookeeper with Spring Cloud Netflix Components</a></span></dt><dd><dl><dt><span class="section"><a href="#_ribbon_with_zookeeper">75.1. Ribbon with Zookeeper</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-zookeeper-service-registry">76. Spring Cloud Zookeeper and Service Registry</a></span></dt><dd><dl><dt><span class="section"><a href="#_instance_status">76.1. Instance Status</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-zookeeper-dependencies">77. Zookeeper Dependencies</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-zookeeper-dependencies-using">77.1. Using the Zookeeper Dependencies</a></span></dt><dt><span class="section"><a href="#spring-cloud-zookeeper-dependencies-activating">77.2. Activating Zookeeper Dependencies</a></span></dt><dt><span class="section"><a href="#spring-cloud-zookeeper-dependencies-setting-up">77.3. Setting up Zookeeper Dependencies</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-zookeeper-dependencies-setting-up-aliases">77.3.1. Aliases</a></span></dt><dt><span class="section"><a href="#_path">77.3.2. Path</a></span></dt><dt><span class="section"><a href="#_load_balancer_type">77.3.3. Load Balancer Type</a></span></dt><dt><span class="section"><a href="#__literal_content_type_literal_template_and_version">77.3.4. <code class="literal">Content-Type</code> Template and Version</a></span></dt><dt><span class="section"><a href="#_default_headers">77.3.5. Default Headers</a></span></dt><dt><span class="section"><a href="#_required_dependencies">77.3.6. Required Dependencies</a></span></dt><dt><span class="section"><a href="#_stubs">77.3.7. Stubs</a></span></dt></dl></dd><dt><span class="section"><a href="#spring-cloud-zookeeper-dependencies-configuring">77.4. Configuring Spring Cloud Zookeeper Dependencies</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-zookeeper-dependency-watcher">78. Spring Cloud Zookeeper Dependency Watcher</a></span></dt><dd><dl><dt><span class="section"><a href="#_activating_2">78.1. Activating</a></span></dt><dt><span class="section"><a href="#_registering_a_listener">78.2. Registering a Listener</a></span></dt><dt><span class="section"><a href="#spring-cloud-zookeeper-dependency-watcher-presence-checker">78.3. Using the Presence Checker</a></span></dt></dl></dd><dt><span class="chapter"><a href="#spring-cloud-zookeeper-config">79. Distributed Configuration with Zookeeper</a></span></dt><dd><dl><dt><span class="section"><a href="#_activating_3">79.1. Activating</a></span></dt><dt><span class="section"><a href="#_customizing_2">79.2. Customizing</a></span></dt><dt><span class="section"><a href="#_access_control_lists_acls">79.3. Access Control Lists (ACLs)</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="#_spring_cloud_security">XI. Spring Cloud Security</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_quickstart">80. Quickstart</a></span></dt><dd><dl><dt><span class="section"><a href="#_oauth2_single_sign_on">80.1. OAuth2 Single Sign On</a></span></dt><dt><span class="section"><a href="#_oauth2_protected_resource">80.2. OAuth2 Protected Resource</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_more_detail">81. More Detail</a></span></dt><dd><dl><dt><span class="section"><a href="#_single_sign_on">81.1. Single Sign On</a></span></dt><dt><span class="section"><a href="#_token_relay">81.2. Token Relay</a></span></dt><dd><dl><dt><span class="section"><a href="#_client_token_relay">81.2.1. Client Token Relay</a></span></dt><dt><span class="section"><a href="#_client_token_relay_in_zuul_proxy">81.2.2. Client Token Relay in Zuul Proxy</a></span></dt><dt><span class="section"><a href="#_resource_server_token_relay">81.2.3. Resource Server Token Relay</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_configuring_authentication_downstream_of_a_zuul_proxy">82. Configuring Authentication Downstream of a Zuul Proxy</a></span></dt></dl></dd><dt><span class="part"><a href="#_spring_cloud_for_cloud_foundry">XII. Spring Cloud for Cloud Foundry</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_discovery">83. Discovery</a></span></dt><dt><span class="chapter"><a href="#_single_sign_on_2">84. Single Sign On</a></span></dt></dl></dd><dt><span class="part"><a href="#_spring_cloud_contract">XIII. Spring Cloud Contract</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_spring_cloud_contract_2">85. Spring Cloud Contract</a></span></dt><dt><span class="chapter"><a href="#_spring_cloud_contract_verifier_introduction">86. Spring Cloud Contract Verifier Introduction</a></span></dt><dd><dl><dt><span class="section"><a href="#_history">86.1. History</a></span></dt><dt><span class="section"><a href="#_why_a_contract_verifier">86.2. Why a Contract Verifier?</a></span></dt><dd><dl><dt><span class="section"><a href="#_testing_issues">86.2.1. Testing issues</a></span></dt></dl></dd><dt><span class="section"><a href="#_purposes">86.3. Purposes</a></span></dt><dt><span class="section"><a href="#_how_it_works">86.4. How It Works</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-contract-verifier-intro-three-second-tour">86.4.1. A Three-second Tour</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-contract-verifier-intro-three-second-tour-producer">On the Producer Side</a></span></dt><dt><span class="section"><a href="#spring-cloud-contract-verifier-intro-three-second-tour-consumer">On the Consumer Side</a></span></dt></dl></dd><dt><span class="section"><a href="#spring-cloud-contract-verifier-intro-three-minute-tour">86.4.2. A Three-minute Tour</a></span></dt><dd><dl><dt><span class="section"><a href="#spring-cloud-contract-verifier-intro-three-minute-tour-producer">On the Producer Side</a></span></dt><dt><span class="section"><a href="#spring-cloud-contract-verifier-intro-three-minute-tour-consumer">On the Consumer Side</a></span></dt></dl></dd><dt><span class="section"><a href="#_defining_the_contract">86.4.3. Defining the Contract</a></span></dt><dt><span class="section"><a href="#_client_side">86.4.4. Client Side</a></span></dt><dt><span class="section"><a href="#_server_side">86.4.5. Server Side</a></span></dt></dl></dd><dt><span class="section"><a href="#_step_by_step_guide_to_consumer_driven_contracts_cdc">86.5. Step-by-step Guide to Consumer Driven Contracts (CDC)</a></span></dt><dd><dl><dt><span class="section"><a href="#_technical_note">86.5.1. Technical note</a></span></dt><dt><span class="section"><a href="#_consumer_side_loan_issuance">86.5.2. Consumer side (Loan Issuance)</a></span></dt><dt><span class="section"><a href="#_producer_side_fraud_detection_server">86.5.3. Producer side (Fraud Detection server)</a></span></dt><dt><span class="section"><a href="#_consumer_side_loan_issuance_final_step">86.5.4. Consumer Side (Loan Issuance) Final Step</a></span></dt></dl></dd><dt><span class="section"><a href="#_dependencies">86.6. Dependencies</a></span></dt><dt><span class="section"><a href="#_additional_links">86.7. Additional Links</a></span></dt><dd><dl><dt><span class="section"><a href="#_spring_cloud_contract_video">86.7.1. Spring Cloud Contract video</a></span></dt><dt><span class="section"><a href="#_readings">86.7.2. Readings</a></span></dt></dl></dd><dt><span class="section"><a href="#_samples_2">86.8. Samples</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_spring_cloud_contract_faq">87. Spring Cloud Contract FAQ</a></span></dt><dd><dl><dt><span class="section"><a href="#_why_use_spring_cloud_contract_verifier_and_not_x">87.1. Why use Spring Cloud Contract Verifier and not X ?</a></span></dt><dt><span class="section"><a href="#_i_don_t_want_to_write_a_contract_in_groovy">87.2. I don&#8217;t want to write a contract in Groovy!</a></span></dt><dt><span class="section"><a href="#_what_is_this_value_consumer_producer">87.3. What is this value(consumer(), producer()) ?</a></span></dt><dt><span class="section"><a href="#_how_to_do_stubs_versioning">87.4. How to do Stubs versioning?</a></span></dt><dd><dl><dt><span class="section"><a href="#_api_versioning">87.4.1. API Versioning</a></span></dt><dt><span class="section"><a href="#_jar_versioning">87.4.2. JAR versioning</a></span></dt><dt><span class="section"><a href="#_dev_or_prod_stubs">87.4.3. Dev or prod stubs</a></span></dt></dl></dd><dt><span class="section"><a href="#_common_repo_with_contracts">87.5. Common repo with contracts</a></span></dt><dd><dl><dt><span class="section"><a href="#_repo_structure">87.5.1. Repo structure</a></span></dt><dt><span class="section"><a href="#_workflow">87.5.2. Workflow</a></span></dt><dt><span class="section"><a href="#_consumer">87.5.3. Consumer</a></span></dt><dt><span class="section"><a href="#_producer">87.5.4. Producer</a></span></dt><dt><span class="section"><a href="#_how_can_i_define_messaging_contracts_per_topic_not_per_producer">87.5.5. How can I define messaging contracts per topic not per producer?</a></span></dt><dd><dl><dt><span class="section"><a href="#_for_maven_project">For Maven Project</a></span></dt><dt><span class="section"><a href="#_for_gradle_project">For Gradle Project</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_do_i_need_a_binary_storage_can_t_i_use_git">87.6. Do I need a Binary Storage? Can&#8217;t I use Git?</a></span></dt><dd><dl><dt><span class="section"><a href="#_protocol_convention">87.6.1. Protocol convention</a></span></dt><dt><span class="section"><a href="#_producer_2">87.6.2. Producer</a></span></dt><dd><dl><dt><span class="section"><a href="#_keeping_contracts_with_the_producer_and_stubs_in_an_external_repository">Keeping contracts with the producer and stubs in an external repository</a></span></dt></dl></dd><dt><span class="section"><a href="#_consumer_2">87.6.3. Consumer</a></span></dt></dl></dd><dt><span class="section"><a href="#_can_i_use_the_pact_broker">87.7. Can I use the Pact Broker?</a></span></dt><dd><dl><dt><span class="section"><a href="#_pact_consumer">87.7.1. Pact Consumer</a></span></dt><dt><span class="section"><a href="#_producer_3">87.7.2. Producer</a></span></dt><dt><span class="section"><a href="#_pact_consumer_producer_contract_approach">87.7.3. Pact Consumer (Producer Contract approach)</a></span></dt></dl></dd><dt><span class="section"><a href="#_how_can_i_debug_the_request_response_being_sent_by_the_generated_tests_client">87.8. How can I debug the request/response being sent by the generated tests client?</a></span></dt><dd><dl><dt><span class="section"><a href="#_how_can_i_debug_the_mapping_request_response_being_sent_by_wiremock">87.8.1. How can I debug the mapping/request/response being sent by WireMock?</a></span></dt><dt><span class="section"><a href="#_how_can_i_see_what_got_registered_in_the_http_server_stub">87.8.2. How can I see what got registered in the HTTP server stub?</a></span></dt><dt><span class="section"><a href="#_can_i_reference_text_from_file">87.8.3. Can I reference text from file?</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_spring_cloud_contract_verifier_setup">88. Spring Cloud Contract Verifier Setup</a></span></dt><dd><dl><dt><span class="section"><a href="#gradle-project">88.1. Gradle Project</a></span></dt><dd><dl><dt><span class="section"><a href="#gradle-prerequisites">88.1.1. Prerequisites</a></span></dt><dt><span class="section"><a href="#gradle-add-gradle-plugin">88.1.2. Add Gradle Plugin with Dependencies</a></span></dt><dt><span class="section"><a href="#gradle-and-rest-assured">88.1.3. Gradle and Rest Assured 2.0</a></span></dt><dt><span class="section"><a href="#gradle-snapshot-versions">88.1.4. Snapshot Versions for Gradle</a></span></dt><dt><span class="section"><a href="#gradle-add-stubs">88.1.5. Add stubs</a></span></dt><dt><span class="section"><a href="#gradle-run-plugin">88.1.6. Run the Plugin</a></span></dt><dt><span class="section"><a href="#gradle-default-setup">88.1.7. Default Setup</a></span></dt><dt><span class="section"><a href="#gradle-configure-plugin">88.1.8. Configure Plugin</a></span></dt><dt><span class="section"><a href="#gradle-configuration-options">88.1.9. Configuration Options</a></span></dt><dt><span class="section"><a href="#gradle-single-base-class">88.1.10. Single Base Class for All Tests</a></span></dt><dt><span class="section"><a href="#gradle-different-base-classes">88.1.11. Different Base Classes for Contracts</a></span></dt><dt><span class="section"><a href="#gradle-invoking-generated-tests">88.1.12. Invoking Generated Tests</a></span></dt><dt><span class="section"><a href="#gradle-pushing-stubs-to-scm">88.1.13. Pushing stubs to SCM</a></span></dt><dt><span class="section"><a href="#gradle-consumer">88.1.14. Spring Cloud Contract Verifier on the Consumer Side</a></span></dt></dl></dd><dt><span class="section"><a href="#maven-project">88.2. Maven Project</a></span></dt><dd><dl><dt><span class="section"><a href="#maven-add-plugin">88.2.1. Add maven plugin</a></span></dt><dt><span class="section"><a href="#maven-rest-assured">88.2.2. Maven and Rest Assured 2.0</a></span></dt><dt><span class="section"><a href="#maven-snapshot-versions">88.2.3. Snapshot versions for Maven</a></span></dt><dt><span class="section"><a href="#maven-add-stubs">88.2.4. Add stubs</a></span></dt><dt><span class="section"><a href="#maven-run-plugin">88.2.5. Run plugin</a></span></dt><dt><span class="section"><a href="#maven-configure-plugin">88.2.6. Configure plugin</a></span></dt><dt><span class="section"><a href="#maven-configuration-options">88.2.7. Configuration Options</a></span></dt><dt><span class="section"><a href="#maven-single-base">88.2.8. Single Base Class for All Tests</a></span></dt><dt><span class="section"><a href="#maven-different-base">88.2.9. Different base classes for contracts</a></span></dt><dt><span class="section"><a href="#maven-invoking-generated-tests">88.2.10. Invoking generated tests</a></span></dt><dt><span class="section"><a href="#maven-pushing-stubs-to-scm">88.2.11. Pushing stubs to SCM</a></span></dt><dt><span class="section"><a href="#maven-sts">88.2.12. Maven Plugin and STS</a></span></dt><dt><span class="section"><a href="#_maven_plugin_with_spock_tests">88.2.13. Maven Plugin with Spock Tests</a></span></dt></dl></dd><dt><span class="section"><a href="#_stubs_and_transitive_dependencies">88.3. Stubs and Transitive Dependencies</a></span></dt><dt><span class="section"><a href="#_scenarios">88.4. Scenarios</a></span></dt><dt><span class="section"><a href="#docker-project">88.5. Docker Project</a></span></dt><dd><dl><dt><span class="section"><a href="#_short_intro_to_maven_jars_and_binary_storage">88.5.1. Short intro to Maven, JARs and Binary storage</a></span></dt><dt><span class="section"><a href="#_how_it_works_2">88.5.2. How it works</a></span></dt><dd><dl><dt><span class="section"><a href="#_environment_variables">Environment Variables</a></span></dt></dl></dd><dt><span class="section"><a href="#_example_of_usage">88.5.3. Example of usage</a></span></dt><dt><span class="section"><a href="#docker-server-side">88.5.4. Server side (nodejs)</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_spring_cloud_contract_verifier_messaging">89. Spring Cloud Contract Verifier Messaging</a></span></dt><dd><dl><dt><span class="section"><a href="#_integrations_2">89.1. Integrations</a></span></dt><dt><span class="section"><a href="#_manual_integration_testing">89.2. Manual Integration Testing</a></span></dt><dt><span class="section"><a href="#_publisher_side_test_generation">89.3. Publisher-Side Test Generation</a></span></dt><dd><dl><dt><span class="section"><a href="#_scenario_1_no_input_message">89.3.1. Scenario 1: No Input Message</a></span></dt><dt><span class="section"><a href="#_scenario_2_output_triggered_by_input">89.3.2. Scenario 2: Output Triggered by Input</a></span></dt><dt><span class="section"><a href="#_scenario_3_no_output_message">89.3.3. Scenario 3: No Output Message</a></span></dt></dl></dd><dt><span class="section"><a href="#_consumer_stub_generation">89.4. Consumer Stub Generation</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_spring_cloud_contract_stub_runner">90. Spring Cloud Contract Stub Runner</a></span></dt><dd><dl><dt><span class="section"><a href="#_snapshot_versions">90.1. Snapshot versions</a></span></dt><dt><span class="section"><a href="#publishing-stubs-as-jars">90.2. Publishing Stubs as JARs</a></span></dt><dt><span class="section"><a href="#_stub_runner_core">90.3. Stub Runner Core</a></span></dt><dd><dl><dt><span class="section"><a href="#_retrieving_stubs">90.3.1. Retrieving stubs</a></span></dt><dd><dl><dt><span class="section"><a href="#_stub_downloading">Stub downloading</a></span></dt><dt><span class="section"><a href="#_classpath_scanning">Classpath scanning</a></span></dt></dl></dd><dt><span class="section"><a href="#_running_stubs">90.3.2. Running stubs</a></span></dt><dd><dl><dt><span class="section"><a href="#_running_using_main_app">Running using main app</a></span></dt><dt><span class="section"><a href="#_http_stubs">HTTP Stubs</a></span></dt><dt><span class="section"><a href="#_viewing_registered_mappings">Viewing registered mappings</a></span></dt><dt><span class="section"><a href="#_messaging_stubs">Messaging Stubs</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_stub_runner_junit_rule_and_stub_runner_junit5_extension">90.4. Stub Runner JUnit Rule and Stub Runner JUnit5 Extension</a></span></dt><dd><dl><dt><span class="section"><a href="#_maven_settings">90.4.1. Maven settings</a></span></dt><dt><span class="section"><a href="#_providing_fixed_ports">90.4.2. Providing fixed ports</a></span></dt><dt><span class="section"><a href="#_fluent_api">90.4.3. Fluent API</a></span></dt><dt><span class="section"><a href="#_stub_runner_with_spring">90.4.4. Stub Runner with Spring</a></span></dt></dl></dd><dt><span class="section"><a href="#_stub_runner_spring_cloud">90.5. Stub Runner Spring Cloud</a></span></dt><dd><dl><dt><span class="section"><a href="#_stubbing_service_discovery">90.5.1. Stubbing Service Discovery</a></span></dt><dd><dl><dt><span class="section"><a href="#_test_profiles_and_service_discovery">Test profiles and service discovery</a></span></dt></dl></dd><dt><span class="section"><a href="#_additional_configuration">90.5.2. Additional Configuration</a></span></dt></dl></dd><dt><span class="section"><a href="#_stub_runner_boot_application">90.6. Stub Runner Boot Application</a></span></dt><dd><dl><dt><span class="section"><a href="#_how_to_use_it">90.6.1. How to use it?</a></span></dt><dd><dl><dt><span class="section"><a href="#_stub_runner_server">Stub Runner Server</a></span></dt><dt><span class="section"><a href="#_stub_runner_server_fat_jar">Stub Runner Server Fat Jar</a></span></dt><dt><span class="section"><a href="#_spring_cloud_cli">Spring Cloud CLI</a></span></dt></dl></dd><dt><span class="section"><a href="#_endpoints_2">90.6.2. Endpoints</a></span></dt><dd><dl><dt><span class="section"><a href="#_http_2">HTTP</a></span></dt><dt><span class="section"><a href="#_messaging_2">Messaging</a></span></dt></dl></dd><dt><span class="section"><a href="#_example">90.6.3. Example</a></span></dt><dt><span class="section"><a href="#_stub_runner_boot_with_service_discovery">90.6.4. Stub Runner Boot with Service Discovery</a></span></dt></dl></dd><dt><span class="section"><a href="#_stubs_per_consumer">90.7. Stubs Per Consumer</a></span></dt><dt><span class="section"><a href="#_common">90.8. Common</a></span></dt><dd><dl><dt><span class="section"><a href="#common-properties-junit-spring">90.8.1. Common Properties for JUnit and Spring</a></span></dt><dt><span class="section"><a href="#stub-runner-stub-ids">90.8.2. Stub Runner Stubs IDs</a></span></dt></dl></dd><dt><span class="section"><a href="#stubrunner-docker">90.9. Stub Runner Docker</a></span></dt><dd><dl><dt><span class="section"><a href="#_how_to_use_it_2">90.9.1. How to use it</a></span></dt><dt><span class="section"><a href="#_example_of_client_side_usage_in_a_non_jvm_project">90.9.2. Example of client side usage in a non JVM project</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#stub-runner-for-messaging">91. Stub Runner for Messaging</a></span></dt><dd><dl><dt><span class="section"><a href="#_stub_triggering">91.1. Stub triggering</a></span></dt><dd><dl><dt><span class="section"><a href="#trigger-label">91.1.1. Trigger by Label</a></span></dt><dt><span class="section"><a href="#trigger-group-artifact-ids">91.1.2. Trigger by Group and Artifact Ids</a></span></dt><dt><span class="section"><a href="#trigger-artifact-ids">91.1.3. Trigger by Artifact Ids</a></span></dt><dt><span class="section"><a href="#trigger-all-messages">91.1.4. Trigger All Messages</a></span></dt></dl></dd><dt><span class="section"><a href="#_stub_runner_camel">91.2. Stub Runner Camel</a></span></dt><dd><dl><dt><span class="section"><a href="#_adding_it_to_the_project">91.2.1. Adding it to the project</a></span></dt><dt><span class="section"><a href="#_disabling_the_functionality">91.2.2. Disabling the functionality</a></span></dt><dt><span class="section"><a href="#_examples">91.2.3. Examples</a></span></dt><dd><dl><dt><span class="section"><a href="#_stubs_structure">Stubs structure</a></span></dt><dt><span class="section"><a href="#_scenario_1_no_input_message_2">Scenario 1 (no input message)</a></span></dt><dt><span class="section"><a href="#_scenario_2_output_triggered_by_input_2">Scenario 2 (output triggered by input)</a></span></dt><dt><span class="section"><a href="#_scenario_3_input_with_no_output">Scenario 3 (input with no output)</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_stub_runner_integration">91.3. Stub Runner Integration</a></span></dt><dd><dl><dt><span class="section"><a href="#_adding_the_runner_to_the_project">91.3.1. Adding the Runner to the Project</a></span></dt><dt><span class="section"><a href="#_disabling_the_functionality_2">91.3.2. Disabling the functionality</a></span></dt><dd><dl><dt><span class="section"><a href="#integration-scenario-1">Scenario 1 (no input message)</a></span></dt><dt><span class="section"><a href="#integration-scenario-2">Scenario 2 (output triggered by input)</a></span></dt><dt><span class="section"><a href="#integration-scenario-3">Scenario 3 (input with no output)</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_stub_runner_stream">91.4. Stub Runner Stream</a></span></dt><dd><dl><dt><span class="section"><a href="#_adding_the_runner_to_the_project_2">91.4.1. Adding the Runner to the Project</a></span></dt><dt><span class="section"><a href="#_disabling_the_functionality_3">91.4.2. Disabling the functionality</a></span></dt><dd><dl><dt><span class="section"><a href="#stream-scenario-1">Scenario 1 (no input message)</a></span></dt><dt><span class="section"><a href="#stream-scenario-2">Scenario 2 (output triggered by input)</a></span></dt><dt><span class="section"><a href="#stream-scenario-3">Scenario 3 (input with no output)</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_stub_runner_spring_amqp">91.5. Stub Runner Spring AMQP</a></span></dt><dd><dl><dt><span class="section"><a href="#_adding_the_runner_to_the_project_3">91.5.1. Adding the Runner to the Project</a></span></dt><dd><dl><dt><span class="section"><a href="#_triggering_the_message">Triggering the message</a></span></dt><dt><span class="section"><a href="#_spring_amqp_test_configuration">Spring AMQP Test Configuration</a></span></dt></dl></dd></dl></dd></dl></dd><dt><span class="chapter"><a href="#contract-dsl">92. Contract DSL</a></span></dt><dd><dl><dt><span class="section"><a href="#_limitations">92.1. Limitations</a></span></dt><dt><span class="section"><a href="#_common_top_level_elements">92.2. Common Top-Level elements</a></span></dt><dd><dl><dt><span class="section"><a href="#contract-dsl-description">92.2.1. Description</a></span></dt><dt><span class="section"><a href="#contract-dsl-name">92.2.2. Name</a></span></dt><dt><span class="section"><a href="#contract-dsl-ignoring-contracts">92.2.3. Ignoring Contracts</a></span></dt><dt><span class="section"><a href="#contract-dsl-passing-values-from-files">92.2.4. Passing Values from Files</a></span></dt><dt><span class="section"><a href="#contract-dsl-http-top-level-elements">92.2.5. HTTP Top-Level Elements</a></span></dt></dl></dd><dt><span class="section"><a href="#_request">92.3. Request</a></span></dt><dt><span class="section"><a href="#_response">92.4. Response</a></span></dt><dt><span class="section"><a href="#_dynamic_properties">92.5. Dynamic properties</a></span></dt><dd><dl><dt><span class="section"><a href="#_dynamic_properties_inside_the_body">92.5.1. Dynamic properties inside the body</a></span></dt><dt><span class="section"><a href="#_regular_expressions">92.5.2. Regular expressions</a></span></dt><dt><span class="section"><a href="#_passing_optional_parameters">92.5.3. Passing Optional Parameters</a></span></dt><dt><span class="section"><a href="#_executing_custom_methods_on_the_server_side">92.5.4. Executing Custom Methods on the Server Side</a></span></dt><dt><span class="section"><a href="#_referencing_the_request_from_the_response">92.5.5. Referencing the Request from the Response</a></span></dt><dt><span class="section"><a href="#_registering_your_own_wiremock_extension">92.5.6. Registering Your Own WireMock Extension</a></span></dt><dt><span class="section"><a href="#contract-matchers">92.5.7. Dynamic Properties in the Matchers Sections</a></span></dt></dl></dd><dt><span class="section"><a href="#_jax_rs_support">92.6. JAX-RS Support</a></span></dt><dt><span class="section"><a href="#_async_support">92.7. Async Support</a></span></dt><dt><span class="section"><a href="#_working_with_context_paths">92.8. Working with Context Paths</a></span></dt><dt><span class="section"><a href="#_working_with_web_flux">92.9. Working with Web Flux</a></span></dt><dt><span class="section"><a href="#_messaging_top_level_elements">92.10. Messaging Top-Level Elements</a></span></dt><dd><dl><dt><span class="section"><a href="#contract-dsl-output-triggered-method">92.10.1. Output Triggered by a Method</a></span></dt><dt><span class="section"><a href="#contract-dsl-output-triggered-message">92.10.2. Output Triggered by a Message</a></span></dt><dt><span class="section"><a href="#contract-dsl-consumer-producer">92.10.3. Consumer/Producer</a></span></dt><dt><span class="section"><a href="#contract-dsl-common">92.10.4. Common</a></span></dt></dl></dd><dt><span class="section"><a href="#_multiple_contracts_in_one_file">92.11. Multiple Contracts in One File</a></span></dt><dt><span class="section"><a href="#_generating_spring_rest_docs_snippets_from_the_contracts">92.12. Generating Spring REST Docs snippets from the contracts</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_customization">93. Customization</a></span></dt><dd><dl><dt><span class="section"><a href="#_extending_the_dsl">93.1. Extending the DSL</a></span></dt><dd><dl><dt><span class="section"><a href="#_common_jar">93.1.1. Common JAR</a></span></dt><dt><span class="section"><a href="#_adding_the_dependency_to_the_project">93.1.2. Adding the Dependency to the Project</a></span></dt><dt><span class="section"><a href="#_test_the_dependency_in_the_project_s_dependencies">93.1.3. Test the Dependency in the Project&#8217;s Dependencies</a></span></dt><dt><span class="section"><a href="#_test_a_dependency_in_the_plugin_s_dependencies">93.1.4. Test a Dependency in the Plugin&#8217;s Dependencies</a></span></dt><dt><span class="section"><a href="#_referencing_classes_in_dsls">93.1.5. Referencing classes in DSLs</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_using_the_pluggable_architecture">94. Using the Pluggable Architecture</a></span></dt><dd><dl><dt><span class="section"><a href="#_custom_contract_converter">94.1. Custom Contract Converter</a></span></dt><dd><dl><dt><span class="section"><a href="#pact-converter">94.1.1. Pact Converter</a></span></dt><dt><span class="section"><a href="#_pact_contract">94.1.2. Pact Contract</a></span></dt><dt><span class="section"><a href="#_pact_for_producers">94.1.3. Pact for Producers</a></span></dt><dt><span class="section"><a href="#_pact_for_consumers">94.1.4. Pact for Consumers</a></span></dt></dl></dd><dt><span class="section"><a href="#_using_the_custom_test_generator">94.2. Using the Custom Test Generator</a></span></dt><dt><span class="section"><a href="#_using_the_custom_stub_generator">94.3. Using the Custom Stub Generator</a></span></dt><dt><span class="section"><a href="#_using_the_custom_stub_runner">94.4. Using the Custom Stub Runner</a></span></dt><dt><span class="section"><a href="#_using_the_custom_stub_downloader">94.5. Using the Custom Stub Downloader</a></span></dt><dt><span class="section"><a href="#scm-stub-downloader">94.6. Using the SCM Stub Downloader</a></span></dt><dt><span class="section"><a href="#pact-stub-downloader">94.7. Using the Pact Stub Downloader</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_spring_cloud_contract_wiremock">95. Spring Cloud Contract WireMock</a></span></dt><dd><dl><dt><span class="section"><a href="#_registering_stubs_automatically">95.1. Registering Stubs Automatically</a></span></dt><dt><span class="section"><a href="#_using_files_to_specify_the_stub_bodies">95.2. Using Files to Specify the Stub Bodies</a></span></dt><dt><span class="section"><a href="#_alternative_using_junit_rules">95.3. Alternative: Using JUnit Rules</a></span></dt><dt><span class="section"><a href="#_relaxed_ssl_validation_for_rest_template">95.4. Relaxed SSL Validation for Rest Template</a></span></dt><dt><span class="section"><a href="#_wiremock_and_spring_mvc_mocks">95.5. WireMock and Spring MVC Mocks</a></span></dt><dt><span class="section"><a href="#_customization_of_wiremock_configuration">95.6. Customization of WireMock configuration</a></span></dt><dt><span class="section"><a href="#_generating_stubs_using_rest_docs">95.7. Generating Stubs using REST Docs</a></span></dt><dt><span class="section"><a href="#_generating_contracts_by_using_rest_docs">95.8. Generating Contracts by Using REST Docs</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_migrations">96. Migrations</a></span></dt><dd><dl><dt><span class="section"><a href="#cloud-verifier-1.0-1.1">96.1. 1.0.x &#8594; 1.1.x</a></span></dt><dd><dl><dt><span class="section"><a href="#_new_structure_of_generated_stubs">96.1.1. New structure of generated stubs</a></span></dt></dl></dd><dt><span class="section"><a href="#cloud-verifier-1.1-1.2">96.2. 1.1.x &#8594; 1.2.x</a></span></dt><dd><dl><dt><span class="section"><a href="#_custom_literal_httpserverstub_literal">96.2.1. Custom <code class="literal">HttpServerStub</code></a></span></dt><dt><span class="section"><a href="#_new_packages_for_generated_tests">96.2.2. New packages for generated tests</a></span></dt><dt><span class="section"><a href="#_new_methods_in_templateprocessor">96.2.3. New Methods in TemplateProcessor</a></span></dt><dt><span class="section"><a href="#_restassured_3_0">96.2.4. RestAssured 3.0</a></span></dt></dl></dd><dt><span class="section"><a href="#cloud-verifier-1.2-2.0">96.3. 1.2.x &#8594; 2.0.x</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_links">97. Links</a></span></dt></dl></dd><dt><span class="part"><a href="#_spring_cloud_vault">XIV. Spring Cloud Vault</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_quick_start_4">98. Quick Start</a></span></dt><dt><span class="chapter"><a href="#_client_side_usage_2">99. Client Side Usage</a></span></dt><dd><dl><dt><span class="section"><a href="#_authentication_2">99.1. Authentication</a></span></dt></dl></dd><dt><span class="chapter"><a href="#vault.config.authentication">100. Authentication methods</a></span></dt><dd><dl><dt><span class="section"><a href="#vault.config.authentication.token">100.1. Token authentication</a></span></dt><dt><span class="section"><a href="#vault.config.authentication.appid">100.2. AppId authentication</a></span></dt><dd><dl><dt><span class="section"><a href="#_custom_userid">100.2.1. Custom UserId</a></span></dt></dl></dd><dt><span class="section"><a href="#_approle_authentication">100.3. AppRole authentication</a></span></dt><dt><span class="section"><a href="#vault.config.authentication.awsec2">100.4. AWS-EC2 authentication</a></span></dt><dt><span class="section"><a href="#vault.config.authentication.awsiam">100.5. AWS-IAM authentication</a></span></dt><dt><span class="section"><a href="#vault.config.authentication.azuremsi">100.6. Azure MSI authentication</a></span></dt><dt><span class="section"><a href="#vault.config.authentication.clientcert">100.7. TLS certificate authentication</a></span></dt><dt><span class="section"><a href="#vault.config.authentication.cubbyhole">100.8. Cubbyhole authentication</a></span></dt></dl></dd><dt><span class="chapter"><a href="#vault.config.authentication.gcpgce">101. GCP-GCE authentication</a></span></dt><dt><span class="chapter"><a href="#vault.config.authentication.gcpiam">102. GCP-IAM authentication</a></span></dt><dd><dl><dt><span class="section"><a href="#vault.config.authentication.kubernetes">102.1. Kubernetes authentication</a></span></dt></dl></dd><dt><span class="chapter"><a href="#vault.config.backends">103. Secret Backends</a></span></dt><dd><dl><dt><span class="section"><a href="#vault.config.backends.generic">103.1. Generic Backend</a></span></dt><dt><span class="section"><a href="#vault.config.backends.kv.versioned">103.2. Versioned Key-Value Backend</a></span></dt><dt><span class="section"><a href="#vault.config.backends.consul">103.3. Consul</a></span></dt><dt><span class="section"><a href="#vault.config.backends.rabbitmq">103.4. RabbitMQ</a></span></dt><dt><span class="section"><a href="#vault.config.backends.aws">103.5. AWS</a></span></dt></dl></dd><dt><span class="chapter"><a href="#vault.config.backends.database-backends">104. Database backends</a></span></dt><dd><dl><dt><span class="section"><a href="#vault.config.backends.database">104.1. Database</a></span></dt><dt><span class="section"><a href="#vault.config.backends.cassandra">104.2. Apache Cassandra</a></span></dt><dt><span class="section"><a href="#vault.config.backends.mongodb">104.3. MongoDB</a></span></dt><dt><span class="section"><a href="#vault.config.backends.mysql">104.4. MySQL</a></span></dt><dt><span class="section"><a href="#vault.config.backends.postgresql">104.5. PostgreSQL</a></span></dt></dl></dd><dt><span class="chapter"><a href="#vault.config.backends.configurer">105. Configure <code class="literal">PropertySourceLocator</code> behavior</a></span></dt><dt><span class="chapter"><a href="#_service_registry_configuration">106. Service Registry Configuration</a></span></dt><dt><span class="chapter"><a href="#vault.config.fail-fast">107. Vault Client Fail Fast</a></span></dt><dt><span class="chapter"><a href="#vault.config.ssl">108. Vault Client SSL configuration</a></span></dt><dt><span class="chapter"><a href="#vault-lease-renewal">109. Lease lifecycle management (renewal and revocation)</a></span></dt></dl></dd><dt><span class="part"><a href="#_spring_cloud_gateway">XV. Spring Cloud Gateway</a></span></dt><dd><dl><dt><span class="chapter"><a href="#gateway-starter">110. How to Include Spring Cloud Gateway</a></span></dt><dt><span class="chapter"><a href="#_glossary">111. Glossary</a></span></dt><dt><span class="chapter"><a href="#gateway-how-it-works">112. How It Works</a></span></dt><dt><span class="chapter"><a href="#gateway-request-predicates-factories">113. Route Predicate Factories</a></span></dt><dd><dl><dt><span class="section"><a href="#_after_route_predicate_factory">113.1. After Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_before_route_predicate_factory">113.2. Before Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_between_route_predicate_factory">113.3. Between Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_cookie_route_predicate_factory">113.4. Cookie Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_header_route_predicate_factory">113.5. Header Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_host_route_predicate_factory">113.6. Host Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_method_route_predicate_factory">113.7. Method Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_path_route_predicate_factory">113.8. Path Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_query_route_predicate_factory">113.9. Query Route Predicate Factory</a></span></dt><dt><span class="section"><a href="#_remoteaddr_route_predicate_factory">113.10. RemoteAddr Route Predicate Factory</a></span></dt><dd><dl><dt><span class="section"><a href="#_modifying_the_way_remote_addresses_are_resolved">113.10.1. Modifying the way remote addresses are resolved</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_gatewayfilter_factories">114. GatewayFilter Factories</a></span></dt><dd><dl><dt><span class="section"><a href="#_addrequestheader_gatewayfilter_factory">114.1. AddRequestHeader GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_addrequestparameter_gatewayfilter_factory">114.2. AddRequestParameter GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_addresponseheader_gatewayfilter_factory">114.3. AddResponseHeader GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#hystrix">114.4. Hystrix GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#fallback-headers">114.5. FallbackHeaders GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_prefixpath_gatewayfilter_factory">114.6. PrefixPath GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_preservehostheader_gatewayfilter_factory">114.7. PreserveHostHeader GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_requestratelimiter_gatewayfilter_factory">114.8. RequestRateLimiter GatewayFilter Factory</a></span></dt><dd><dl><dt><span class="section"><a href="#_redis_ratelimiter">114.8.1. Redis RateLimiter</a></span></dt></dl></dd><dt><span class="section"><a href="#_redirectto_gatewayfilter_factory">114.9. RedirectTo GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_removenonproxyheaders_gatewayfilter_factory">114.10. RemoveNonProxyHeaders GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_removerequestheader_gatewayfilter_factory">114.11. RemoveRequestHeader GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_removeresponseheader_gatewayfilter_factory">114.12. RemoveResponseHeader GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_rewritepath_gatewayfilter_factory">114.13. RewritePath GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_rewriteresponseheader_gatewayfilter_factory">114.14. RewriteResponseHeader GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_savesession_gatewayfilter_factory">114.15. SaveSession GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_secureheaders_gatewayfilter_factory">114.16. SecureHeaders GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_setpath_gatewayfilter_factory">114.17. SetPath GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_setresponseheader_gatewayfilter_factory">114.18. SetResponseHeader GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_setstatus_gatewayfilter_factory">114.19. SetStatus GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_stripprefix_gatewayfilter_factory">114.20. StripPrefix GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_retry_gatewayfilter_factory">114.21. Retry GatewayFilter Factory</a></span></dt><dt><span class="section"><a href="#_requestsize_gatewayfilter_factory">114.22. RequestSize GatewayFilter Factory</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_global_filters">115. Global Filters</a></span></dt><dd><dl><dt><span class="section"><a href="#_combined_global_filter_and_gatewayfilter_ordering">115.1. Combined Global Filter and GatewayFilter Ordering</a></span></dt><dt><span class="section"><a href="#_forward_routing_filter">115.2. Forward Routing Filter</a></span></dt><dt><span class="section"><a href="#_loadbalancerclient_filter">115.3. LoadBalancerClient Filter</a></span></dt><dt><span class="section"><a href="#_netty_routing_filter">115.4. Netty Routing Filter</a></span></dt><dt><span class="section"><a href="#_netty_write_response_filter">115.5. Netty Write Response Filter</a></span></dt><dt><span class="section"><a href="#_routetorequesturl_filter">115.6. RouteToRequestUrl Filter</a></span></dt><dt><span class="section"><a href="#_websocket_routing_filter">115.7. Websocket Routing Filter</a></span></dt><dt><span class="section"><a href="#_gateway_metrics_filter">115.8. Gateway Metrics Filter</a></span></dt><dt><span class="section"><a href="#_making_an_exchange_as_routed">115.9. Making An Exchange As Routed</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_tls_ssl">116. TLS / SSL</a></span></dt><dd><dl><dt><span class="section"><a href="#_tls_handshake">116.1. TLS Handshake</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_configuration_2">117. Configuration</a></span></dt><dd><dl><dt><span class="section"><a href="#_fluent_java_routes_api">117.1. Fluent Java Routes API</a></span></dt><dt><span class="section"><a href="#_discoveryclient_route_definition_locator">117.2. DiscoveryClient Route Definition Locator</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_reactor_netty_access_logs">118. Reactor Netty Access Logs</a></span></dt><dt><span class="chapter"><a href="#_cors_configuration">119. CORS Configuration</a></span></dt><dt><span class="chapter"><a href="#_actuator_api">120. Actuator API</a></span></dt><dt><span class="chapter"><a href="#_developer_guide">121. Developer Guide</a></span></dt><dd><dl><dt><span class="section"><a href="#_writing_custom_route_predicate_factories">121.1. Writing Custom Route Predicate Factories</a></span></dt><dt><span class="section"><a href="#_writing_custom_gatewayfilter_factories">121.2. Writing Custom GatewayFilter Factories</a></span></dt><dt><span class="section"><a href="#_writing_custom_global_filters">121.3. Writing Custom Global Filters</a></span></dt><dt><span class="section"><a href="#_writing_custom_route_locators_and_writers">121.4. Writing Custom Route Locators and Writers</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_building_a_simple_gateway_using_spring_mvc_or_webflux">122. Building a Simple Gateway Using Spring MVC or Webflux</a></span></dt></dl></dd><dt><span class="part"><a href="#_spring_cloud_function_2">XVI. Spring Cloud Function</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_introduction_2">123. Introduction</a></span></dt><dt><span class="chapter"><a href="#_getting_started">124. Getting Started</a></span></dt><dt><span class="chapter"><a href="#_building_and_running_a_function">125. Building and Running a Function</a></span></dt><dt><span class="chapter"><a href="#_function_catalog_and_flexible_function_signatures">126. Function Catalog and Flexible Function Signatures</a></span></dt><dd><dl><dt><span class="section"><a href="#_java_8_function_support">126.1. Java 8 function support</a></span></dt><dt><span class="section"><a href="#_kotlin_lambda_support">126.2. Kotlin Lambda support</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_standalone_web_applications">127. Standalone Web Applications</a></span></dt><dt><span class="chapter"><a href="#_standalone_streaming_applications">128. Standalone Streaming Applications</a></span></dt><dt><span class="chapter"><a href="#_deploying_a_packaged_function">129. Deploying a Packaged Function</a></span></dt><dt><span class="chapter"><a href="#_functional_bean_definitions">130. Functional Bean Definitions</a></span></dt><dd><dl><dt><span class="section"><a href="#_comparing_functional_with_traditional_bean_definitions">130.1. Comparing Functional with Traditional Bean Definitions</a></span></dt><dt><span class="section"><a href="#_testing_functional_applications">130.2. Testing Functional Applications</a></span></dt><dt><span class="section"><a href="#_limitations_of_functional_bean_declaration">130.3. Limitations of Functional Bean Declaration</a></span></dt></dl></dd><dt><span class="chapter"><a href="#_dynamic_compilation">131. Dynamic Compilation</a></span></dt><dt><span class="chapter"><a href="#_serverless_platform_adapters">132. Serverless Platform Adapters</a></span></dt><dd><dl><dt><span class="section"><a href="#_aws_lambda">132.1. AWS Lambda</a></span></dt><dd><dl><dt><span class="section"><a href="#_introduction_3">132.1.1. Introduction</a></span></dt><dt><span class="section"><a href="#_notes_on_jar_layout">132.1.2. Notes on JAR Layout</a></span></dt><dt><span class="section"><a href="#_upload">132.1.3. Upload</a></span></dt><dt><span class="section"><a href="#_platfom_specific_features">132.1.4. Platfom Specific Features</a></span></dt><dd><dl><dt><span class="section"><a href="#_http_and_api_gateway">HTTP and API Gateway</a></span></dt></dl></dd></dl></dd><dt><span class="section"><a href="#_azure_functions">132.2. Azure Functions</a></span></dt><dd><dl><dt><span class="section"><a href="#_notes_on_jar_layout_2">132.2.1. Notes on JAR Layout</a></span></dt><dt><span class="section"><a href="#_build">132.2.2. Build</a></span></dt><dt><span class="section"><a href="#_running_the_sample">132.2.3. Running the sample</a></span></dt></dl></dd><dt><span class="section"><a href="#_apache_openwhisk">132.3. Apache Openwhisk</a></span></dt><dd><dl><dt><span class="section"><a href="#_quick_start_5">132.3.1. Quick Start</a></span></dt></dl></dd></dl></dd></dl></dd><dt><span class="part"><a href="#_spring_cloud_kubernetes">XVII. Spring Cloud Kubernetes</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_why_do_you_need_spring_cloud_kubernetes">133. Why do you need Spring Cloud Kubernetes?</a></span></dt></dl></dd><dt><span class="part"><a href="#_discoveryclient_for_kubernetes">XVIII. DiscoveryClient for Kubernetes</a></span></dt><dt><span class="part"><a href="#_kubernetes_propertysource_implementations">XIX. Kubernetes PropertySource implementations</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_configmap_propertysource">134. ConfigMap PropertySource</a></span></dt><dt><span class="chapter"><a href="#_secrets_propertysource">135. Secrets PropertySource</a></span></dt><dt><span class="chapter"><a href="#_propertysource_reload">136. PropertySource Reload</a></span></dt></dl></dd><dt><span class="part"><a href="#_ribbon_discovery_in_kubernetes">XX. Ribbon discovery in Kubernetes</a></span></dt><dt><span class="part"><a href="#_kubernetes_awareness">XXI. Kubernetes Awareness</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_kubernetes_profile_autoconfiguration">137. Kubernetes Profile Autoconfiguration</a></span></dt></dl></dd><dt><span class="part"><a href="#_pod_health_indicator">XXII. Pod Health Indicator</a></span></dt><dt><span class="part"><a href="#_leader_election">XXIII. Leader Election</a></span></dt><dt><span class="part"><a href="#_security_configurations_inside_kubernetes">XXIV. Security Configurations inside Kubernetes</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_namespace">138. Namespace</a></span></dt><dt><span class="chapter"><a href="#_service_account">139. Service Account</a></span></dt></dl></dd><dt><span class="part"><a href="#_examples_2">XXV. Examples</a></span></dt><dt><span class="part"><a href="#_other_resources">XXVI. Other Resources</a></span></dt><dd><dl><dt><span class="chapter"><a href="#_building">140. Building</a></span></dt><dd><dl><dt><span class="section"><a href="#_basic_compile_and_test">140.1. Basic Compile and Test</a></span></dt><dt><span class="section"><a href="#_documentation">140.2. Documentation</a></span></dt><dt><span class="section"><a href="#_working_with_the_code">140.3. Working with the code</a></span></dt><dd><dl><dt><span class="section"><a href="#_importing_into_eclipse_with_m2eclipse">140.3.1. Importing into eclipse with m2eclipse</a></span></dt><dt><span class="section"><a href="#_importing_into_eclipse_without_m2eclipse">140.3.2. Importing into eclipse without m2eclipse</a></span></dt></dl></dd></dl></dd><dt><span class="chapter"><a href="#_contributing">141. Contributing</a></span></dt><dd><dl><dt><span class="section"><a href="#_sign_the_contributor_license_agreement">141.1. Sign the Contributor License Agreement</a></span></dt><dt><span class="section"><a href="#_code_of_conduct">141.2. Code of Conduct</a></span></dt><dt><span class="section"><a href="#_code_conventions_and_housekeeping">141.3. Code Conventions and Housekeeping</a></span></dt></dl></dd></dl></dd><dt><span class="part"><a href="#_appendix_compendium_of_configuration_properties">XXVII. Appendix: Compendium of Configuration Properties</a></span></dt></dl></div><div class="preface"><div class="titlepage"><div><div><h1 class="title"><a name="d0e9" href="#d0e9"></a></h1></div></div></div><p>Spring Cloud provides tools for developers to quickly build some of
the common patterns in distributed systems (e.g. configuration
management, service discovery, circuit breakers, intelligent routing,
micro-proxy, control bus). Coordination of
distributed systems leads to boiler plate patterns, and using Spring
Cloud developers can quickly stand up services and applications that
implement those patterns. They will work well in any distributed
environment, including the developer&#8217;s own laptop, bare metal data
centres, and managed platforms such as Cloud Foundry.</p><p>Version: Greenwich.M3</p></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_features" href="#_features"></a>1.&nbsp;Features</h1></div></div></div><p>Spring Cloud focuses on providing good out of box experience for typical use cases
and extensibility mechanism to cover others.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Distributed/versioned configuration</li><li class="listitem">Service registration and discovery</li><li class="listitem">Routing</li><li class="listitem">Service-to-service calls</li><li class="listitem">Load balancing</li><li class="listitem">Circuit Breakers</li><li class="listitem">Distributed messaging</li></ul></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_cloud_native_applications" href="#_cloud_native_applications"></a>Part&nbsp;I.&nbsp;Cloud Native Applications</h1></div></div></div><div class="partintro"><div></div><p><a class="link" href="http://pivotal.io/platform-as-a-service/migrating-to-cloud-native-application-architectures-ebook" target="_top">Cloud Native</a> is a style of application development that encourages easy adoption of best practices in the areas of continuous delivery and value-driven development.
A related discipline is that of building <a class="link" href="http://12factor.net/" target="_top">12-factor Applications</a>, in which development practices are aligned with delivery and operations goals&#8201;&#8212;&#8201;for instance, by using declarative programming and management and monitoring.
Spring Cloud facilitates these styles of development in a number of specific ways.
The starting point is a set of features to which all components in a distributed system need easy access.</p><p>Many of those features are covered by <a class="link" href="http://projects.spring.io/spring-boot" target="_top">Spring Boot</a>, on which Spring Cloud builds. Some more features are delivered by Spring Cloud as two libraries: Spring Cloud Context and Spring Cloud Commons.
Spring Cloud Context provides utilities and special services for the <code class="literal">ApplicationContext</code> of a Spring Cloud application (bootstrap context, encryption, refresh scope, and environment endpoints). Spring Cloud Commons is a set of abstractions and common classes used in different Spring Cloud implementations (such as Spring Cloud Netflix and Spring Cloud Consul).</p><p>If you get an exception due to "Illegal key size" and you use Sun&#8217;s JDK, you need to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files.
See the following links for more information:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html" target="_top">Java 6 JCE</a></li><li class="listitem"><a class="link" href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html" target="_top">Java 7 JCE</a></li><li class="listitem"><a class="link" href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html" target="_top">Java 8 JCE</a></li></ul></div><p>Extract the files into the JDK/jre/lib/security folder for whichever version of JRE/JDK x64/x86 you use.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Spring Cloud is released under the non-restrictive Apache 2.0 license.
If you would like to contribute to this section of the documentation or if you find an error, you can find the source code and issue trackers for the project at <a class="link" href="https://github.com/spring-cloud/spring-cloud-commons/tree/master/docs/src/main/asciidoc" target="_top">github</a>.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_context_application_context_services" href="#_spring_cloud_context_application_context_services"></a>2.&nbsp;Spring Cloud Context: Application Context Services</h2></div></div></div><p>Spring Boot has an opinionated view of how to build an application with Spring.
For instance, it has conventional locations for common configuration files and has endpoints for common management and monitoring tasks.
Spring Cloud builds on top of that and adds a few features that probably all components in a system would use or occasionally need.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_the_bootstrap_application_context" href="#_the_bootstrap_application_context"></a>2.1&nbsp;The Bootstrap Application Context</h2></div></div></div><p>A Spring Cloud application operates by creating a &#8220;bootstrap&#8221; context, which is a parent context for the main application.
It is responsible for loading configuration properties from the external sources and for decrypting properties in the local external configuration files.
The two contexts share an <code class="literal">Environment</code>, which is the source of external properties for any Spring application.
By default, bootstrap properties (not <code class="literal">bootstrap.properties</code> but properties that are loaded during the bootstrap phase) are added with high precedence, so they cannot be overridden by local configuration.</p><p>The bootstrap context uses a different convention for locating external configuration than the main application context.
Instead of <code class="literal">application.yml</code> (or <code class="literal">.properties</code>), you can use <code class="literal">bootstrap.yml</code>, keeping the external configuration for bootstrap and main context
nicely separate.
The following listing shows an example:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="screen">spring:
application:
name: foo
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}</pre><p>
</p><p>If your application needs any application-specific configuration from the server, it is a good idea to set the <code class="literal">spring.application.name</code> (in <code class="literal">bootstrap.yml</code> or <code class="literal">application.yml</code>).</p><p>You can disable the bootstrap process completely by setting <code class="literal">spring.cloud.bootstrap.enabled=false</code> (for example, in system properties).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_application_context_hierarchies" href="#_application_context_hierarchies"></a>2.2&nbsp;Application Context Hierarchies</h2></div></div></div><p>If you build an application context from <code class="literal">SpringApplication</code> or <code class="literal">SpringApplicationBuilder</code>, then the Bootstrap context is added as a parent to that context.
It is a feature of Spring that child contexts inherit property sources and profiles from their parent, so the &#8220;main&#8221; application context contains additional property sources, compared to building the same context without Spring Cloud Config.
The additional property sources are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">&#8220;bootstrap&#8221;: If any <code class="literal">PropertySourceLocators</code> are found in the Bootstrap context and if they have non-empty properties, an optional <code class="literal">CompositePropertySource</code> appears with high priority.
An example would be properties from the Spring Cloud Config Server.
See &#8220;<a class="xref" href="#customizing-bootstrap-property-sources" title="2.6&nbsp;Customizing the Bootstrap Property Sources">Section&nbsp;2.6, &#8220;Customizing the Bootstrap Property Sources&#8221;</a>&#8221; for instructions on how to customize the contents of this property source.</li><li class="listitem">&#8220;applicationConfig: [classpath:bootstrap.yml]&#8221; (and related files if Spring profiles are active): If you have a <code class="literal">bootstrap.yml</code> (or <code class="literal">.properties</code>), those properties are used to configure the Bootstrap context.
Then they get added to the child context when its parent is set.
They have lower precedence than the <code class="literal">application.yml</code> (or <code class="literal">.properties</code>) and any other property sources that are added to the child as a normal part of the process of creating a Spring Boot application.
See &#8220;<a class="xref" href="#customizing-bootstrap-properties" title="2.3&nbsp;Changing the Location of Bootstrap Properties">Section&nbsp;2.3, &#8220;Changing the Location of Bootstrap Properties&#8221;</a>&#8221; for instructions on how to customize the contents of these property sources.</li></ul></div><p>Because of the ordering rules of property sources, the &#8220;bootstrap&#8221; entries take precedence.
However, note that these do not contain any data from <code class="literal">bootstrap.yml</code>, which has very low precedence but can be used to set defaults.</p><p>You can extend the context hierarchy by setting the parent context of any <code class="literal">ApplicationContext</code> you create&#8201;&#8212;&#8201;for example, by using its own interface or with the <code class="literal">SpringApplicationBuilder</code> convenience methods (<code class="literal">parent()</code>, <code class="literal">child()</code> and <code class="literal">sibling()</code>).
The bootstrap context is the parent of the most senior ancestor that you create yourself.
Every context in the hierarchy has its own &#8220;bootstrap&#8221; (possibly empty) property source to avoid promoting values inadvertently from parents down to their descendants.
If there is a Config Server, every context in the hierarchy can also (in principle) have a different <code class="literal">spring.application.name</code> and, hence, a different remote property source.
Normal Spring application context behavior rules apply to property resolution: properties from a child context override those in
the parent, by name and also by property source name.
(If the child has a property source with the same name as the parent, the value from the parent is not included in the child).</p><p>Note that the <code class="literal">SpringApplicationBuilder</code> lets you share an <code class="literal">Environment</code> amongst the whole hierarchy, but that is not the default.
Thus, sibling contexts, in particular, do not need to have the same profiles or property sources, even though they may share common values with their parent.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="customizing-bootstrap-properties" href="#customizing-bootstrap-properties"></a>2.3&nbsp;Changing the Location of Bootstrap Properties</h2></div></div></div><p>The <code class="literal">bootstrap.yml</code> (or <code class="literal">.properties</code>) location can be specified by setting <code class="literal">spring.cloud.bootstrap.name</code> (default: <code class="literal">bootstrap</code>) or <code class="literal">spring.cloud.bootstrap.location</code> (default: empty)&#8201;&#8212;&#8201;for example, in System properties.
Those properties behave like the <code class="literal">spring.config.*</code> variants with the same name.
In fact, they are used to set up the bootstrap <code class="literal">ApplicationContext</code> by setting those properties in its <code class="literal">Environment</code>.
If there is an active profile (from <code class="literal">spring.profiles.active</code> or through the <code class="literal">Environment</code> API in the
context you are building), properties in that profile get loaded as well, the same as in a regular Spring Boot app&#8201;&#8212;&#8201;for example, from <code class="literal">bootstrap-development.properties</code> for a <code class="literal">development</code> profile.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="overriding-bootstrap-properties" href="#overriding-bootstrap-properties"></a>2.4&nbsp;Overriding the Values of Remote Properties</h2></div></div></div><p>The property sources that are added to your application by the bootstrap context are often &#8220;remote&#8221; (from example, from Spring Cloud Config Server).
By default, they cannot be overridden locally.
If you want to let your applications override the remote properties with their own System properties or config files, the remote property source has to grant it permission by setting <code class="literal">spring.cloud.config.allowOverride=true</code> (it does not work to set this locally).
Once that flag is set, two finer-grained settings control the location of the remote properties in relation to system properties and the application&#8217;s local configuration:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">spring.cloud.config.overrideNone=true</code>: Override from any local property source.</li><li class="listitem"><code class="literal">spring.cloud.config.overrideSystemProperties=false</code>: Only system properties, command line arguments, and environment variables (but not the local config files) should override the remote settings.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customizing_the_bootstrap_configuration" href="#_customizing_the_bootstrap_configuration"></a>2.5&nbsp;Customizing the Bootstrap Configuration</h2></div></div></div><p>The bootstrap context can be set to do anything you like by adding entries to <code class="literal">/META-INF/spring.factories</code> under a key named <code class="literal">org.springframework.cloud.bootstrap.BootstrapConfiguration</code>.
This holds a comma-separated list of Spring <code class="literal">@Configuration</code> classes that are used to create the context.
Any beans that you want to be available to the main application context for autowiring can be created here.
There is a special contract for <code class="literal">@Beans</code> of type <code class="literal">ApplicationContextInitializer</code>.
If you want to control the startup sequence, classes can be marked with an <code class="literal">@Order</code> annotation (the default order is <code class="literal">last</code>).</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>When adding custom <code class="literal">BootstrapConfiguration</code>, be careful that the classes you add are not <code class="literal">@ComponentScanned</code> by mistake into your &#8220;main&#8221; application context, where they might not be needed.
Use a separate package name for boot configuration classes and make sure that name is not already covered by your <code class="literal">@ComponentScan</code> or <code class="literal">@SpringBootApplication</code> annotated configuration classes.</p></td></tr></table></div><p>The bootstrap process ends by injecting initializers into the main <code class="literal">SpringApplication</code> instance (which is the normal Spring Boot startup sequence, whether it is running as a standalone application or deployed in an application server).
First, a bootstrap context is created from the classes found in <code class="literal">spring.factories</code>.
Then, all <code class="literal">@Beans</code> of type <code class="literal">ApplicationContextInitializer</code> are added to the main <code class="literal">SpringApplication</code> before it is started.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="customizing-bootstrap-property-sources" href="#customizing-bootstrap-property-sources"></a>2.6&nbsp;Customizing the Bootstrap Property Sources</h2></div></div></div><p>The default property source for external configuration added by the bootstrap process is the Spring Cloud Config Server, but you can add additional sources by adding beans of type <code class="literal">PropertySourceLocator</code> to the bootstrap context (through <code class="literal">spring.factories</code>).
For instance, you can insert additional properties from a different server or from a database.</p><p>As an example, consider the following custom locator:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomPropertySourceLocator <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> PropertySourceLocator {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> PropertySource&lt;?&gt; locate(Environment environment) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> MapPropertySource(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"customProperty"</span>,
Collections.&lt;String, Object&gt;singletonMap(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"property.from.sample.custom.source"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"worked as intended"</span>));
}
}</pre><p>The <code class="literal">Environment</code> that is passed in is the one for the <code class="literal">ApplicationContext</code> about to be created&#8201;&#8212;&#8201;in other words, the one for which we supply additional property sources for.
It already has its normal Spring Boot-provided property sources, so you can use those to locate a property source specific to this <code class="literal">Environment</code> (for example, by keying it on <code class="literal">spring.application.name</code>, as is done in the default Spring Cloud Config Server property source locator).</p><p>If you create a jar with this class in it and then add a <code class="literal">META-INF/spring.factories</code> containing the following, the <code class="literal">customProperty</code> <code class="literal">PropertySource</code> appears in any application that includes that jar on its classpath:</p><pre class="screen">org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_logging_configuration" href="#_logging_configuration"></a>2.7&nbsp;Logging Configuration</h2></div></div></div><p>If you are going to use Spring Boot to configure log settings than
you should place this configuration in `bootstrap.[yml | properties]
if you would like it to apply to all events.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>For Spring Cloud to initialize logging configuration properly you cannot use a custom prefix. For example,
using <code class="literal">custom.loggin.logpath</code> will not be recognized by Spring Cloud when initializing the logging system.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_environment_changes" href="#_environment_changes"></a>2.8&nbsp;Environment Changes</h2></div></div></div><p>The application listens for an <code class="literal">EnvironmentChangeEvent</code> and reacts to the change in a couple of standard ways (additional <code class="literal">ApplicationListeners</code> can be added as <code class="literal">@Beans</code> by the user in the normal way).
When an <code class="literal">EnvironmentChangeEvent</code> is observed, it has a list of key values that have changed, and the application uses those to:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Re-bind any <code class="literal">@ConfigurationProperties</code> beans in the context</li><li class="listitem">Set the logger levels for any properties in <code class="literal">logging.level.*</code></li></ul></div><p>Note that the Config Client does not, by default, poll for changes in the <code class="literal">Environment</code>.
Generally, we would not recommend that approach for detecting changes (although you could set it up with a
<code class="literal">@Scheduled</code> annotation).
If you have a scaled-out client application, it is better to broadcast the <code class="literal">EnvironmentChangeEvent</code> to all the instances instead of having them polling for changes (for example, by using the <a class="link" href="https://github.com/spring-cloud/spring-cloud-bus" target="_top">Spring Cloud Bus</a>).</p><p>The <code class="literal">EnvironmentChangeEvent</code> covers a large class of refresh use cases, as long as you can actually make a change to the <code class="literal">Environment</code> and publish the event.
Note that those APIs are public and part of core Spring).
You can verify that the changes are bound to <code class="literal">@ConfigurationProperties</code> beans by visiting the <code class="literal">/configprops</code> endpoint (a normal Spring Boot Actuator feature).
For instance, a <code class="literal">DataSource</code> can have its <code class="literal">maxPoolSize</code> changed at runtime (the default <code class="literal">DataSource</code> created by Spring Boot is an <code class="literal">@ConfigurationProperties</code> bean) and grow capacity dynamically.
Re-binding <code class="literal">@ConfigurationProperties</code> does not cover another large class of use cases, where you need more control over the refresh and where you need a change to be atomic over the whole <code class="literal">ApplicationContext</code>.
To address those concerns, we have <code class="literal">@RefreshScope</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="refresh-scope" href="#refresh-scope"></a>2.9&nbsp;Refresh Scope</h2></div></div></div><p>When there is a configuration change, a Spring <code class="literal">@Bean</code> that is marked as <code class="literal">@RefreshScope</code> gets special treatment.
This feature addresses the problem of stateful beans that only get their configuration injected when they are initialized.
For instance, if a <code class="literal">DataSource</code> has open connections when the database URL is changed via the <code class="literal">Environment</code>, you probably want the holders of those connections to be able to complete what they are doing.
Then, the next time something borrows a connection from the pool, it gets one with the new URL.</p><p>Sometimes, it might even be mandatory to apply the <code class="literal">@RefreshScope</code>
annotation on some beans which can be only initialized once. If a bean
is "immutable", you will have to either annotate the bean with <code class="literal">@RefreshScope</code>
or specify the classname under the property key
<code class="literal">spring.cloud.refresh.extra-refreshable</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you create a <code class="literal">DataSource</code> bean yourself and the implementation is a <code class="literal">HikariDataSource</code>, return the
most specific type, in this case <code class="literal">HikariDataSource</code>. Otherwise, you will need to set
<code class="literal">spring.cloud.refresh.extra-refreshable=javax.sql.DataSource</code>.</p></td></tr></table></div><p>Refresh scope beans are lazy proxies that initialize when they are used (that is, when a method is called), and the scope acts as a cache of initialized values.
To force a bean to re-initialize on the next method call, you must invalidate its cache entry.</p><p>The <code class="literal">RefreshScope</code> is a bean in the context and has a public <code class="literal">refreshAll()</code> method to refresh all beans in the scope by clearing the target cache.
The <code class="literal">/refresh</code> endpoint exposes this functionality (over HTTP or JMX).
To refresh an individual bean by name, there is also a <code class="literal">refresh(String)</code> method.</p><p>To expose the <code class="literal">/refresh</code> endpoint, you need to add following configuration to your application:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">management</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> endpoints</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> web</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> exposure</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> include</span>: refresh</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><code class="literal">@RefreshScope</code> works (technically) on an <code class="literal">@Configuration</code> class, but it might lead to surprising behavior.
For example, it does not mean that all the <code class="literal">@Beans</code> defined in that class are themselves in <code class="literal">@RefreshScope</code>.
Specifically, anything that depends on those beans cannot rely on them being updated when a refresh is initiated, unless it is itself in <code class="literal">@RefreshScope</code>.
In that case, it is rebuilt on a refresh and its dependencies are re-injected. At that point, they are re-initialized from the refreshed <code class="literal">@Configuration</code>).</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_encryption_and_decryption" href="#_encryption_and_decryption"></a>2.10&nbsp;Encryption and Decryption</h2></div></div></div><p>Spring Cloud has an <code class="literal">Environment</code> pre-processor for decrypting property values locally.
It follows the same rules as the Config Server and has the same external configuration through <code class="literal">encrypt.*</code>.
Thus, you can use encrypted values in the form of <code class="literal">{cipher}*</code> and, as long as there is a valid key, they are decrypted before the main application context gets the <code class="literal">Environment</code> settings.
To use the encryption features in an application, you need to include Spring Security RSA in your classpath (Maven co-ordinates: "org.springframework.security:spring-security-rsa"), and you also need the full strength JCE extensions in your JVM.</p><p>If you get an exception due to "Illegal key size" and you use Sun&#8217;s JDK, you need to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files.
See the following links for more information:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html" target="_top">Java 6 JCE</a></li><li class="listitem"><a class="link" href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html" target="_top">Java 7 JCE</a></li><li class="listitem"><a class="link" href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html" target="_top">Java 8 JCE</a></li></ul></div><p>Extract the files into the JDK/jre/lib/security folder for whichever version of JRE/JDK x64/x86 you use.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_endpoints" href="#_endpoints"></a>2.11&nbsp;Endpoints</h2></div></div></div><p>For a Spring Boot Actuator application, some additional management endpoints are available. You can use:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">POST</code> to <code class="literal">/actuator/env</code> to update the <code class="literal">Environment</code> and rebind <code class="literal">@ConfigurationProperties</code> and log levels.</li><li class="listitem"><code class="literal">/actuator/refresh</code> to re-load the boot strap context and refresh the <code class="literal">@RefreshScope</code> beans.</li><li class="listitem"><code class="literal">/actuator/restart</code> to close the <code class="literal">ApplicationContext</code> and restart it (disabled by default).</li><li class="listitem"><code class="literal">/actuator/pause</code> and <code class="literal">/actuator/resume</code> for calling the <code class="literal">Lifecycle</code> methods (<code class="literal">stop()</code> and <code class="literal">start()</code> on the <code class="literal">ApplicationContext</code>).</li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you disable the <code class="literal">/actuator/restart</code> endpoint then the <code class="literal">/actuator/pause</code> and <code class="literal">/actuator/resume</code> endpoints
will also be disabled since they are just a special case of <code class="literal">/actuator/restart</code>.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_commons_common_abstractions" href="#_spring_cloud_commons_common_abstractions"></a>3.&nbsp;Spring Cloud Commons: Common Abstractions</h2></div></div></div><p>Patterns such as service discovery, load balancing, and circuit breakers lend themselves to a common abstraction layer that can be consumed by all Spring Cloud clients, independent of the implementation (for example, discovery with Eureka or Consul).</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="__enablediscoveryclient" href="#__enablediscoveryclient"></a>3.1&nbsp;@EnableDiscoveryClient</h2></div></div></div><p>Spring Cloud Commons provides the <code class="literal">@EnableDiscoveryClient</code> annotation.
This looks for implementations of the <code class="literal">DiscoveryClient</code> interface with <code class="literal">META-INF/spring.factories</code>.
Implementations of the Discovery Client add a configuration class to <code class="literal">spring.factories</code> under the <code class="literal">org.springframework.cloud.client.discovery.EnableDiscoveryClient</code> key.
Examples of <code class="literal">DiscoveryClient</code> implementations include <a class="link" href="http://cloud.spring.io/spring-cloud-netflix/" target="_top">Spring Cloud Netflix Eureka</a>, <a class="link" href="http://cloud.spring.io/spring-cloud-consul/" target="_top">Spring Cloud Consul Discovery</a>, and <a class="link" href="http://cloud.spring.io/spring-cloud-zookeeper/" target="_top">Spring Cloud Zookeeper Discovery</a>.</p><p>By default, implementations of <code class="literal">DiscoveryClient</code> auto-register the local Spring Boot server with the remote discovery server.
This behavior can be disabled by setting <code class="literal">autoRegister=false</code> in <code class="literal">@EnableDiscoveryClient</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><code class="literal">@EnableDiscoveryClient</code> is no longer required.
You can put a <code class="literal">DiscoveryClient</code> implementation on the classpath to cause the Spring Boot application to register with the service discovery server.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_health_indicator" href="#_health_indicator"></a>3.1.1&nbsp;Health Indicator</h3></div></div></div><p>Commons creates a Spring Boot <code class="literal">HealthIndicator</code> that <code class="literal">DiscoveryClient</code> implementations can participate in by implementing <code class="literal">DiscoveryHealthIndicator</code>.
To disable the composite <code class="literal">HealthIndicator</code>, set <code class="literal">spring.cloud.discovery.client.composite-indicator.enabled=false</code>.
A generic <code class="literal">HealthIndicator</code> based on <code class="literal">DiscoveryClient</code> is auto-configured (<code class="literal">DiscoveryClientHealthIndicator</code>).
To disable it, set <code class="literal">spring.cloud.discovery.client.health-indicator.enabled=false</code>.
To disable the description field of the <code class="literal">DiscoveryClientHealthIndicator</code>, set <code class="literal">spring.cloud.discovery.client.health-indicator.include-description=false</code>.
Otherwise, it can bubble up as the <code class="literal">description</code> of the rolled up <code class="literal">HealthIndicator</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_ordering_literal_discoveryclient_literal_instances" href="#_ordering_literal_discoveryclient_literal_instances"></a>3.1.2&nbsp;Ordering <code class="literal">DiscoveryClient</code> instances</h3></div></div></div><p><code class="literal">DiscoveryClient</code> interface extends <code class="literal">Ordered</code>. This is useful when using multiple discovery
clients, as it allows you to define the order of the returned discovery clients, similar to
how you can order the beans loaded by a Spring application. By default, the order of any <code class="literal">DiscoveryClient</code> is set to
<code class="literal">0</code>. If you want to set a different order for your custom <code class="literal">DiscoveryClient</code> implementations, you just need to override
the <code class="literal">getOrder()</code> method so that it returns the value that is suitable for your setup. Apart from this, you can use
properties to set the order of the <code class="literal">DiscoveryClient</code>
implementations provided by Spring Cloud, among others <code class="literal">ConsulDiscoveryClient</code>, <code class="literal">EurekaDiscoveryClient</code> and
<code class="literal">ZookeeperDiscoveryClient</code>. In order to do it, you just need to set the
<code class="literal">spring.cloud.{clientIdentifier}.discovery.order</code> (or <code class="literal">eureka.client.order</code> for Eureka) property to the desired value.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_serviceregistry" href="#_serviceregistry"></a>3.2&nbsp;ServiceRegistry</h2></div></div></div><p>Commons now provides a <code class="literal">ServiceRegistry</code> interface that provides methods such as <code class="literal">register(Registration)</code> and <code class="literal">deregister(Registration)</code>, which let you provide custom registered services.
<code class="literal">Registration</code> is a marker interface.</p><p>The following example shows the <code class="literal">ServiceRegistry</code> in use:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableDiscoveryClient(autoRegister=false)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfiguration {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> ServiceRegistry registry;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> MyConfiguration(ServiceRegistry registry) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.registry = registry;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// called through some external process, such as an event or a custom actuator endpoint</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> register() {
Registration registration = constructRegistration();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.registry.register(registration);
}
}</pre><p>Each <code class="literal">ServiceRegistry</code> implementation has its own <code class="literal">Registry</code> implementation.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">ZookeeperRegistration</code> used with <code class="literal">ZookeeperServiceRegistry</code></li><li class="listitem"><code class="literal">EurekaRegistration</code> used with <code class="literal">EurekaServiceRegistry</code></li><li class="listitem"><code class="literal">ConsulRegistration</code> used with <code class="literal">ConsulServiceRegistry</code></li></ul></div><p>If you are using the <code class="literal">ServiceRegistry</code> interface, you are going to need to pass the
correct <code class="literal">Registry</code> implementation for the <code class="literal">ServiceRegistry</code> implementation you
are using.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_serviceregistry_auto_registration" href="#_serviceregistry_auto_registration"></a>3.2.1&nbsp;ServiceRegistry Auto-Registration</h3></div></div></div><p>By default, the <code class="literal">ServiceRegistry</code> implementation auto-registers the running service.
To disable that behavior, you can set:
* <code class="literal">@EnableDiscoveryClient(autoRegister=false)</code> to permanently disable auto-registration.
* <code class="literal">spring.cloud.service-registry.auto-registration.enabled=false</code> to disable the behavior through configuration.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_serviceregistry_auto_registration_events" href="#_serviceregistry_auto_registration_events"></a>ServiceRegistry Auto-Registration Events</h4></div></div></div><p>There are two events that will be fired when a service auto-registers. The first event, called
<code class="literal">InstancePreRegisteredEvent</code>, is fired before the service is registered. The second
event, called <code class="literal">InstanceRegisteredEvent</code>, is fired after the service is registered. You can register an
<code class="literal">ApplicationListener</code>(s) to listen to and react to these events.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>These events will not be fired if <code class="literal">spring.cloud.service-registry.auto-registration.enabled</code> is set to <code class="literal">false</code>.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_service_registry_actuator_endpoint" href="#_service_registry_actuator_endpoint"></a>3.2.2&nbsp;Service Registry Actuator Endpoint</h3></div></div></div><p>Spring Cloud Commons provides a <code class="literal">/service-registry</code> actuator endpoint.
This endpoint relies on a <code class="literal">Registration</code> bean in the Spring Application Context.
Calling <code class="literal">/service-registry</code> with GET returns the status of the <code class="literal">Registration</code>.
Using POST to the same endpoint with a JSON body changes the status of the current <code class="literal">Registration</code> to the new value.
The JSON body has to include the <code class="literal">status</code> field with the preferred value.
Please see the documentation of the <code class="literal">ServiceRegistry</code> implementation you use for the allowed values when updating the status and the values returned for the status.
For instance, Eureka&#8217;s supported statuses are <code class="literal">UP</code>, <code class="literal">DOWN</code>, <code class="literal">OUT_OF_SERVICE</code>, and <code class="literal">UNKNOWN</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_spring_resttemplate_as_a_load_balancer_client" href="#_spring_resttemplate_as_a_load_balancer_client"></a>3.3&nbsp;Spring RestTemplate as a Load Balancer Client</h2></div></div></div><p><code class="literal">RestTemplate</code> can be automatically configured to use ribbon.
To create a load-balanced <code class="literal">RestTemplate</code>, create a <code class="literal">RestTemplate</code> <code class="literal">@Bean</code> and use the <code class="literal">@LoadBalanced</code> qualifier, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfiguration {
<em><span class="hl-annotation" style="color: gray">@LoadBalanced</span></em>
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
RestTemplate restTemplate() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> RestTemplate();
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyClass {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RestTemplate restTemplate;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String doOtherStuff() {
String results = restTemplate.getForObject(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://stores/stores"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> results;
}
}</pre><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p>A <code class="literal">RestTemplate</code> bean is no longer created through auto-configuration.
Individual applications must create it.</p></td></tr></table></div><p>The URI needs to use a virtual host name (that is, a service name, not a host name).
The Ribbon client is used to create a full physical address.
See <a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java" target="_top">RibbonAutoConfiguration</a> for details of how the <code class="literal">RestTemplate</code> is set up.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_spring_webclient_as_a_load_balancer_client" href="#_spring_webclient_as_a_load_balancer_client"></a>3.4&nbsp;Spring WebClient as a Load Balancer Client</h2></div></div></div><p><code class="literal">WebClient</code> can be automatically configured to use the <code class="literal">LoadBalancerClient</code>.
To create a load-balanced <code class="literal">WebClient</code>, create a <code class="literal">WebClient.Builder</code> <code class="literal">@Bean</code> and use the <code class="literal">@LoadBalanced</code> qualifier, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@LoadBalanced</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> WebClient.Builder loadBalancedWebClientBuilder() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> WebClient.builder();
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyClass {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> WebClient.Builder webClientBuilder;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Mono&lt;String&gt; doOtherStuff() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> webClientBuilder.build().get().uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://stores/stores"</span>)
.retrieve().bodyToMono(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
}</pre><p>The URI needs to use a virtual host name (that is, a service name, not a host name).
The Ribbon client is used to create a full physical address.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_retrying_failed_requests" href="#_retrying_failed_requests"></a>3.4.1&nbsp;Retrying Failed Requests</h3></div></div></div><p>A load-balanced <code class="literal">RestTemplate</code> can be configured to retry failed requests.
By default, this logic is disabled.
You can enable it by adding <a class="link" href="https://github.com/spring-projects/spring-retry" target="_top">Spring Retry</a> to your application&#8217;s classpath.
The load-balanced <code class="literal">RestTemplate</code> honors some of the Ribbon configuration values related to retrying failed requests.
You can use <code class="literal">client.ribbon.MaxAutoRetries</code>, <code class="literal">client.ribbon.MaxAutoRetriesNextServer</code>, and <code class="literal">client.ribbon.OkToRetryOnAllOperations</code> properties.
If you would like to disable the retry logic with Spring Retry on the classpath, you can set <code class="literal">spring.cloud.loadbalancer.retry.enabled=false</code>.
See the <a class="link" href="https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties" target="_top">Ribbon documentation</a> for a description of what these properties do.</p><p>If you would like to implement a <code class="literal">BackOffPolicy</code> in your retries, you need to create a bean of type <code class="literal">LoadBalancedRetryFactory</code> and override the <code class="literal">createBackOffPolicy</code> method:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
LoadBalancedRetryFactory retryFactory() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LoadBalancedRetryFactory() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> BackOffPolicy createBackOffPolicy(String service) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ExponentialBackOffPolicy();
}
};
}
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><code class="literal">client</code> in the preceding examples should be replaced with your Ribbon client&#8217;s name.</p></td></tr></table></div><p>If you want to add one or more <code class="literal">RetryListener</code> implementations to your retry functionality, you need to
create a bean of type <code class="literal">LoadBalancedRetryListenerFactory</code> and return the <code class="literal">RetryListener</code> array
you would like to use for a given service, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
LoadBalancedRetryListenerFactory retryListenerFactory() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LoadBalancedRetryListenerFactory() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> RetryListener[] createRetryListeners(String service) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> RetryListener[]{<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> RetryListener() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> &lt;T, E <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Throwable&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> open(RetryContext context, RetryCallback&lt;T, E&gt; callback) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//TODO Do you business...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> true;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> &lt;T, E <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Throwable&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> close(RetryContext context, RetryCallback&lt;T, E&gt; callback, Throwable throwable) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//TODO Do you business...</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> &lt;T, E <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Throwable&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> onError(RetryContext context, RetryCallback&lt;T, E&gt; callback, Throwable throwable) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//TODO Do you business...</span>
}
}};
}
};
}
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_multiple_resttemplate_objects" href="#_multiple_resttemplate_objects"></a>3.5&nbsp;Multiple RestTemplate objects</h2></div></div></div><p>If you want a <code class="literal">RestTemplate</code> that is not load-balanced, create a <code class="literal">RestTemplate</code> bean and inject it.
To access the load-balanced <code class="literal">RestTemplate</code>, use the <code class="literal">@LoadBalanced</code> qualifier when you create your <code class="literal">@Bean</code>, as shown in the following example:\</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfiguration {
<em><span class="hl-annotation" style="color: gray">@LoadBalanced</span></em>
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
RestTemplate loadBalanced() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> RestTemplate();
}
<em><span class="hl-annotation" style="color: gray">@Primary</span></em>
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
RestTemplate restTemplate() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> RestTemplate();
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyClass {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RestTemplate restTemplate;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<em><span class="hl-annotation" style="color: gray">@LoadBalanced</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RestTemplate loadBalanced;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String doOtherStuff() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> loadBalanced.getForObject(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://stores/stores"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String doStuff() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> restTemplate.getForObject(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://example.com"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Notice the use of the <code class="literal">@Primary</code> annotation on the plain <code class="literal">RestTemplate</code> declaration in the preceding example to disambiguate the unqualified <code class="literal">@Autowired</code> injection.</p></td></tr></table></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>If you see errors such as <code class="literal">java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89</code>, try injecting <code class="literal">RestOperations</code> or setting <code class="literal">spring.aop.proxyTargetClass=true</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="loadbalanced-webclient" href="#loadbalanced-webclient"></a>3.6&nbsp;Spring WebFlux WebClient as a Load Balancer Client</h2></div></div></div><p><code class="literal">WebClient</code> can be configured to use the <code class="literal">LoadBalancerClient</code>. <code class="literal">LoadBalancerExchangeFilterFunction</code> is auto-configured if <code class="literal">spring-webflux</code> is on the classpath. The following example shows how to configure a <code class="literal">WebClient</code> to use load balancer:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyClass {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> LoadBalancerExchangeFilterFunction lbFunction;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Mono&lt;String&gt; doOtherStuff() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> WebClient.builder().baseUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://stores"</span>)
.filter(lbFunction)
.build()
.get()
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/stores"</span>)
.retrieve()
.bodyToMono(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
}</pre><p>The URI needs to use a virtual host name (that is, a service name, not a host name).
The <code class="literal">LoadBalancerClient</code> is used to create a full physical address.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="ignore-network-interfaces" href="#ignore-network-interfaces"></a>3.7&nbsp;Ignore Network Interfaces</h2></div></div></div><p>Sometimes, it is useful to ignore certain named network interfaces so that they can be excluded from Service Discovery registration (for example, when running in a Docker container).
A list of regular expressions can be set to cause the desired network interfaces to be ignored.
The following configuration ignores the <code class="literal">docker0</code> interface and all interfaces that start with <code class="literal">veth</code>:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*</pre><p>
</p><p>You can also force the use of only specified network addresses by using a list of regular expressions, as shown in the following example:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
inetutils:
preferredNetworks:
- 192.168
- 10.0</pre><p>
</p><p>You can also force the use of only site-local addresses, as shown in the following example:
.application.yml</p><pre class="screen">spring:
cloud:
inetutils:
useOnlySiteLocalInterfaces: true</pre><p>See <a class="link" href="https://docs.oracle.com/javase/8/docs/api/java/net/Inet4Address.html#isSiteLocalAddress--" target="_top">Inet4Address.html.isSiteLocalAddress()</a> for more details about what constitutes a site-local address.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="http-clients" href="#http-clients"></a>3.8&nbsp;HTTP Client Factories</h2></div></div></div><p>Spring Cloud Commons provides beans for creating both Apache HTTP clients (<code class="literal">ApacheHttpClientFactory</code>) and OK HTTP clients (<code class="literal">OkHttpClientFactory</code>).
The <code class="literal">OkHttpClientFactory</code> bean is created only if the OK HTTP jar is on the classpath.
In addition, Spring Cloud Commons provides beans for creating the connection managers used by both clients: <code class="literal">ApacheHttpClientConnectionManagerFactory</code> for the Apache HTTP client and <code class="literal">OkHttpClientConnectionPoolFactory</code> for the OK HTTP client.
If you would like to customize how the HTTP clients are created in downstream projects, you can provide your own implementation of these beans.
In addition, if you provide a bean of type <code class="literal">HttpClientBuilder</code> or <code class="literal">OkHttpClient.Builder</code>, the default factories use these builders as the basis for the builders returned to downstream projects.
You can also disable the creation of these beans by setting <code class="literal">spring.cloud.httpclientfactories.apache.enabled</code> or <code class="literal">spring.cloud.httpclientfactories.ok.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="enabled-features" href="#enabled-features"></a>3.9&nbsp;Enabled Features</h2></div></div></div><p>Spring Cloud Commons provides a <code class="literal">/features</code> actuator endpoint.
This endpoint returns features available on the classpath and whether they are enabled.
The information returned includes the feature type, name, version, and vendor.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_feature_types" href="#_feature_types"></a>3.9.1&nbsp;Feature types</h3></div></div></div><p>There are two types of 'features': abstract and named.</p><p>Abstract features are features where an interface or abstract class is defined and that an implementation the creates, such as <code class="literal">DiscoveryClient</code>, <code class="literal">LoadBalancerClient</code>, or <code class="literal">LockService</code>.
The abstract class or interface is used to find a bean of that type in the context.
The version displayed is <code class="literal">bean.getClass().getPackage().getImplementationVersion()</code>.</p><p>Named features are features that do not have a particular class they implement, such as "Circuit Breaker", "API Gateway", "Spring Cloud Bus", and others. These features require a name and a bean type.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_declaring_features" href="#_declaring_features"></a>3.9.2&nbsp;Declaring features</h3></div></div></div><p>Any module can declare any number of <code class="literal">HasFeature</code> beans, as shown in the following examples:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> HasFeatures commonsFeatures() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> HasFeatures.abstractFeatures(DiscoveryClient.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, LoadBalancerClient.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> HasFeatures consulFeatures() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> HasFeatures.namedFeatures(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> NamedFeature(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Spring Cloud Bus"</span>, ConsulBusAutoConfiguration.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>),
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> NamedFeature(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Circuit Breaker"</span>, HystrixCommandAspect.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>));
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
HasFeatures localFeatures() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> HasFeatures.builder()
.abstractFeature(Foo.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
.namedFeature(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> NamedFeature(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Bar Feature"</span>, Bar.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>))
.abstractFeature(Baz.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
.build();
}</pre><p>Each of these beans should go in an appropriately guarded <code class="literal">@Configuration</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_spring_cloud_compatibility_verification" href="#_spring_cloud_compatibility_verification"></a>3.10&nbsp;Spring Cloud Compatibility Verification</h2></div></div></div><p>Due to the fact that some users have problem with setting up Spring Cloud application, we&#8217;ve decided
to add a compatibility verification mechanism. It will break if your current setup is not compatible
with Spring Cloud requirements, together with a report, showing what exactly went wrong.</p><p>At the moment we verify which version of Spring Boot is added to your classpath.</p><p>Example of a report</p><pre class="screen">***************************
APPLICATION FAILED TO START
***************************
Description:
Your project setup is incompatible with our requirements due to following reasons:
- Spring Boot [2.1.0.RELEASE] is not compatible with this Spring Cloud release train
Action:
Consider applying the following actions:
- Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] .
You can find the latest Spring Boot versions here [https://spring.io/projects/spring-boot#learn].
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://spring.io/projects/spring-cloud#overview] and check the [Release Trains] section.</pre><p>In order to disable this feature, set <code class="literal">spring.cloud.compatibility-verifier.enabled</code> to <code class="literal">false</code>.
If you want to override the compatible Spring Boot versions, just set the
<code class="literal">spring.cloud.compatibility-verifier.compatible-boot-versions</code> property with a comma separated list
of compatible Spring Boot versions.</p></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_config" href="#_spring_cloud_config"></a>Part&nbsp;II.&nbsp;Spring Cloud Config</h1></div></div></div><div class="partintro"><div></div><p><span class="strong"><strong>Greenwich.M3</strong></span></p><p>Spring Cloud Config provides server-side and client-side support for externalized configuration in a distributed system. With the Config Server, you have a central place to manage external properties for applications across all environments.
The concepts on both client and server map identically to the Spring <code class="literal">Environment</code> and <code class="literal">PropertySource</code> abstractions, so they fit very well with Spring applications but can be used with any application running in any language.
As an application moves through the deployment pipeline from dev to test and into production, you can manage the configuration between those environments and be certain that applications have everything they need to run when they migrate.
The default implementation of the server storage backend uses git, so it easily supports labelled versions of configuration environments as well as being accessible to a wide range of tooling for managing the content.
It is easy to add alternative implementations and plug them in with Spring configuration.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_quick_start" href="#_quick_start"></a>4.&nbsp;Quick Start</h2></div></div></div><p>This quick start walks through using both the server and the client of Spring Cloud Config Server.</p><p>First, start the server, as follows:</p><pre class="screen">$ cd spring-cloud-config-server
$ ../mvnw spring-boot:run</pre><p>The server is a Spring Boot application, so you can run it from your IDE if you prefer to do so (the main class is <code class="literal">ConfigServerApplication</code>).</p><p>Next try out a client, as follows:</p><pre class="screen">$ curl localhost:8888/foo/development
{"name":"foo","label":"master","propertySources":[
{"name":"https://github.com/scratches/config-repo/foo-development.properties","source":{"bar":"spam"}},
{"name":"https://github.com/scratches/config-repo/foo.properties","source":{"foo":"bar"}}
]}</pre><p>The default strategy for locating property sources is to clone a git repository (at <code class="literal">spring.cloud.config.server.git.uri</code>) and use it to initialize a mini <code class="literal">SpringApplication</code>.
The mini-application&#8217;s <code class="literal">Environment</code> is used to enumerate property sources and publish them at a JSON endpoint.</p><p>The HTTP service has resources in the following form:</p><pre class="screen">/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
/{application}-{profile}.properties
/{label}/{application}-{profile}.properties</pre><p>where <code class="literal">application</code> is injected as the <code class="literal">spring.config.name</code> in the <code class="literal">SpringApplication</code> (what is normally <code class="literal">application</code> in a regular Spring Boot app), <code class="literal">profile</code> is an active profile (or comma-separated list of properties), and <code class="literal">label</code> is an optional git label (defaults to <code class="literal">master</code>.)</p><p>Spring Cloud Config Server pulls configuration for remote clients from a git repository (which must be provided), as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo</pre><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_client_side_usage" href="#_client_side_usage"></a>4.1&nbsp;Client Side Usage</h2></div></div></div><p>To use these features in an application, you can build it as a Spring Boot application that depends on spring-cloud-config-client (for an example, see the test cases for the config-client or the sample application).
The most convenient way to add the dependency is with a Spring Boot starter <code class="literal">org.springframework.cloud:spring-cloud-starter-config</code>.
There is also a parent pom and BOM (<code class="literal">spring-cloud-starter-parent</code>) for Maven users and a Spring IO version management properties file for Gradle and Spring CLI users. The following example shows a typical Maven configuration:</p><p><b>pom.xml.&nbsp;</b>
</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-parent<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>{spring-boot-docs-version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;relativePath /&gt;</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- lookup parent from repository --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>{spring-cloud-version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-config<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- repositories also needed for snapshots and milestones --&gt;</span></pre><p>
</p><p>Now you can create a standard Spring Boot application, such as the following HTTP server:</p><pre class="screen">@SpringBootApplication
@RestController
public class Application {
@RequestMapping("/")
public String home() {
return "Hello World!";
}
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}</pre><p>When this HTTP server runs, it picks up the external configuration from the default local config server (if it is running) on port 8888.
To modify the startup behavior, you can change the location of the config server by using <code class="literal">bootstrap.properties</code> (similar to <code class="literal">application.properties</code> but for the bootstrap phase of an application context), as shown in the following example:</p><pre class="screen">spring.cloud.config.uri: http://myconfigserver.com</pre><p>By default, if no application name is set, <code class="literal">application</code> will be used. To modify the name, the following property can be added to the <code class="literal">bootstrap.properties</code> file:</p><pre class="screen">spring.application.name: myapp</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When setting the property <code class="literal">${spring.application.name}</code> do not prefix your app name with the reserved word <code class="literal">application-</code> to prevent issues resolving the correct property source.</p></td></tr></table></div><p>The bootstrap properties show up in the <code class="literal">/env</code> endpoint as a high-priority property source, as shown in the following example.</p><pre class="screen">$ curl localhost:8080/env
{
"profiles":[],
"configService:https://github.com/spring-cloud-samples/config-repo/bar.properties":{"foo":"bar"},
"servletContextInitParams":{},
"systemProperties":{...},
...
}</pre><p>A property source called <code class="literal">``configService:&lt;URL of remote repository&gt;/&lt;file name&gt;</code> contains the <code class="literal">foo</code> property with a value of <code class="literal">bar</code> and is highest priority.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The URL in the property source name is the git repository, not the config server URL.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_config_server" href="#_spring_cloud_config_server"></a>5.&nbsp;Spring Cloud Config Server</h2></div></div></div><p>Spring Cloud Config Server provides an HTTP resource-based API for external configuration (name-value pairs or equivalent YAML content).
The server is embeddable in a Spring Boot application, by using the <code class="literal">@EnableConfigServer</code> annotation.
Consequently, the following application is a config server:</p><p><b>ConfigServer.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableConfigServer</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ConfigServer {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(ConfigServer.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre><p>
</p><p>Like all Spring Boot applications, it runs on port 8080 by default, but you can switch it to the more conventional port 8888 in various ways.
The easiest, which also sets a default configuration repository, is by launching it with <code class="literal">spring.config.name=configserver</code> (there is a <code class="literal">configserver.yml</code> in the Config Server jar).
Another is to use your own <code class="literal">application.properties</code>, as shown in the following example:</p><p><b>application.properties.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">server.port</span>: 8888
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.config.server.git.uri</span>: file://${user.home}/config-repo</pre><p>
</p><p>where <code class="literal">${user.home}/config-repo</code> is a git repository containing YAML and properties files.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>On Windows, you need an extra "/" in the file URL if it is absolute with a drive prefix (for example,<code class="literal"><a class="link" href="file:///${user.home}/config-repo" target="_top">file:///${user.home}/config-repo</a></code>).</p></td></tr></table></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>The following listing shows a recipe for creating the git repository in the preceding example:</p><pre class="screen">$ cd $HOME
$ mkdir config-repo
$ cd config-repo
$ git init .
$ echo info.foo: bar &gt; application.properties
$ git add -A .
$ git commit -m "Add application.properties"</pre></td></tr></table></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Using the local filesystem for your git repository is intended for testing only.
You should use a server to host your configuration repositories in production.</p></td></tr></table></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>The initial clone of your configuration repository can be quick and efficient if you keep only text files in it.
If you store binary files, especially large ones, you may experience delays on the first request for configuration or encounter out of memory errors in the server.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_environment_repository" href="#_environment_repository"></a>5.1&nbsp;Environment Repository</h2></div></div></div><p>Where should you store the configuration data for the Config Server?
The strategy that governs this behaviour is the <code class="literal">EnvironmentRepository</code>, serving <code class="literal">Environment</code> objects.
This <code class="literal">Environment</code> is a shallow copy of the domain from the Spring <code class="literal">Environment</code> (including <code class="literal">propertySources</code> as the main feature).
The <code class="literal">Environment</code> resources are parametrized by three variables:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">{application}</code>, which maps to <code class="literal">spring.application.name</code> on the client side.</li><li class="listitem"><code class="literal">{profile}</code>, which maps to <code class="literal">spring.profiles.active</code> on the client (comma-separated list).</li><li class="listitem"><code class="literal">{label}</code>, which is a server side feature labelling a "versioned" set of config files.</li></ul></div><p>Repository implementations generally behave like a Spring Boot application, loading configuration files from a <code class="literal">spring.config.name</code> equal to the <code class="literal">{application}</code> parameter, and <code class="literal">spring.profiles.active</code> equal to the <code class="literal">{profiles}</code> parameter.
Precedence rules for profiles are also the same as in a regular Spring Boot application: Active profiles take precedence over defaults, and, if there are multiple profiles, the last one wins (similar to adding entries to a <code class="literal">Map</code>).</p><p>The following sample client application has this bootstrap configuration:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: foo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> active</span>: dev,mysql</pre><p>
</p><p>(As usual with a Spring Boot application, these properties could also be set by environment variables or command line arguments).</p><p>If the repository is file-based, the server creates an
<code class="literal">Environment</code> from <code class="literal">application.yml</code> (shared between all clients) and
<code class="literal">foo.yml</code> (with <code class="literal">foo.yml</code> taking precedence).
If the YAML files have documents inside them that point to Spring profiles, those are applied with higher precedence (in order of the profiles listed).
If there are profile-specific YAML (or properties) files, these are also applied with higher precedence than the defaults.
Higher precedence translates to a <code class="literal">PropertySource</code> listed earlier in the <code class="literal">Environment</code>.
(These same rules apply in a standalone Spring Boot application.)</p><p>You can set spring.cloud.config.server.accept-empty to false so that Server would return a HTTP 404 status, if the application is not found.By default, this flag is set to true.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_git_backend" href="#_git_backend"></a>5.1.1&nbsp;Git Backend</h3></div></div></div><p>The default implementation of <code class="literal">EnvironmentRepository</code> uses a Git backend, which is very convenient for managing upgrades and physical environments and for auditing changes.
To change the location of the repository, you can set the <code class="literal">spring.cloud.config.server.git.uri</code> configuration property in the Config Server (for example in <code class="literal">application.yml</code>).
If you set it with a <code class="literal">file:</code> prefix, it should work from a local repository so that you can get started quickly and easily without a server. However, in that case, the server operates directly on the local repository without cloning it (it does not matter if it is not bare because the Config Server never makes changes to the "remote" repository).
To scale the Config Server up and make it highly available, you need to have all instances of the server pointing to the same repository, so only a shared file system would work.
Even in that case, it is better to use the <code class="literal">ssh:</code> protocol for a shared filesystem repository, so that the server can clone it and use a local working copy as a cache.</p><p>This repository implementation maps the <code class="literal">{label}</code> parameter of the HTTP resource to a git label (commit id, branch name, or tag).
If the git branch or tag name contains a slash (<code class="literal">/</code>), then the label in the HTTP URL should instead be specified with the special string <code class="literal">(_)</code> (to avoid ambiguity with other URL paths).
For example, if the label is <code class="literal">foo/bar</code>, replacing the slash would result in the following label: <code class="literal">foo(_)bar</code>.
The inclusion of the special string <code class="literal">(_)</code> can also be applied to the <code class="literal">{application}</code> parameter.
If you use a command-line client such as curl, be careful with the brackets in the URL&#8201;&#8212;&#8201;you should escape them from the shell with single quotes ('').</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_skipping_ssl_certificate_validation" href="#_skipping_ssl_certificate_validation"></a>Skipping SSL Certificate Validation</h4></div></div></div><p>The configuration server&#8217;s validation of the Git server&#8217;s SSL certificate can be disabled by setting the <code class="literal">git.skipSslValidation</code> property to <code class="literal">true</code> (default is <code class="literal">false</code>).</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://example.com/my/repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> skipSslValidation</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_setting_http_connection_timeout" href="#_setting_http_connection_timeout"></a>Setting HTTP Connection Timeout</h4></div></div></div><p>You can configure the time, in seconds, that the configuration server will wait to acquire an HTTP connection. Use the <code class="literal">git.timeout</code> property.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://example.com/my/repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> timeout</span>: <span class="hl-number">4</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_placeholders_in_git_uri" href="#_placeholders_in_git_uri"></a>Placeholders in Git URI</h4></div></div></div><p>Spring Cloud Config Server supports a git repository URL with placeholders for the <code class="literal">{application}</code> and <code class="literal">{profile}</code> (and <code class="literal">{label}</code> if you need it, but remember that the label is applied as a git label anyway).
So you can support a &#8220;one repository per application&#8221; policy by using a structure similar to the following:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/myorg/{application<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>You can also support a &#8220;one repository per profile&#8221; policy by using a similar pattern but with
<code class="literal">{profile}</code>.</p><p>Additionally, using the special string "(_)" within your <code class="literal">{application}</code> parameters can enable support for multiple
organizations, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/{application<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>where <code class="literal">{application}</code> is provided at request time in the following format: <code class="literal">organization(_)application</code>.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_pattern_matching_and_multiple_repositories" href="#_pattern_matching_and_multiple_repositories"></a>Pattern Matching and Multiple Repositories</h4></div></div></div><p>Spring Cloud Config also includes support for more complex requirements with pattern
matching on the application and profile name.
The pattern format is a comma-separated list of <code class="literal">{application}/{profile}</code> names with wildcards (note that a pattern beginning with a wildcard may need to be quoted), as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repos</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> simple</span>: https://github.com/simple/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> special</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>: special*/dev*,*special*/dev*
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/special/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> local</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>: local*
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: file:/home/configsvc/config-repo</pre><p>If <code class="literal">{application}/{profile}</code> does not match any of the patterns, it uses the default URI defined under <code class="literal">spring.cloud.config.server.git.uri</code>.
In the above example, for the &#8220;simple&#8221; repository, the pattern is <code class="literal">simple/*</code> (it only matches one application named <code class="literal">simple</code> in all profiles). The &#8220;local&#8221; repository matches all application names beginning with <code class="literal">local</code> in all profiles (the <code class="literal">/*</code> suffix is added automatically to any pattern that does not have a profile matcher).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The &#8220;one-liner&#8221; short cut used in the &#8220;simple&#8221; example can be used only if the only property to be set is the URI.
If you need to set anything else (credentials, pattern, and so on) you need to use the full form.</p></td></tr></table></div><p>The <code class="literal">pattern</code> property in the repo is actually an array, so you can use a YAML array (or <code class="literal">[0]</code>, <code class="literal">[1]</code>, etc. suffixes in properties files) to bind to multiple patterns.
You may need to do so if you are going to run apps with multiple profiles, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repos</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> development</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>:
- <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'*/development'</span>
- <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'*/staging'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/development/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> staging</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>:
- <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'*/qa'</span>
- <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'*/production'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/staging/config-repo</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Spring Cloud guesses that a pattern containing a profile that does not end in <code class="literal">*</code> implies that you actually want to match a list of profiles starting with this pattern (so <code class="literal">*/staging</code> is a shortcut for <code class="literal">["*/staging", "*/staging,*"]</code>, and so on).
This is common where, for instance, you need to run applications in the &#8220;development&#8221; profile locally but also the &#8220;cloud&#8221; profile remotely.</p></td></tr></table></div><p>Every repository can also optionally store config files in sub-directories, and patterns to search for those directories can be specified as <code class="literal">searchPaths</code>.
The following example shows a config file at the top level:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> searchPaths</span>: foo,bar*</pre><p>In the preceding example, the server searches for config files in the top level and in the <code class="literal">foo/</code> sub-directory and also any sub-directory whose name begins with <code class="literal">bar</code>.</p><p>By default, the server clones remote repositories when configuration
is first requested.
The server can be configured to clone the repositories at startup, as shown in the following top-level example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://git/common/config-repo.git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repos</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> team-a</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>: team-a-*
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloneOnStart</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://git/team-a/config-repo.git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> team-b</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>: team-b-*
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloneOnStart</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://git/team-b/config-repo.git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> team-c</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>: team-c-*
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://git/team-a/config-repo.git</pre><p>In the preceding example, the server clones team-a&#8217;s config-repo on startup, before it
accepts any requests.
All other repositories are not cloned until configuration from the repository is requested.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Setting a repository to be cloned when the Config Server starts up can help to identify a misconfigured configuration source (such as an invalid repository URI) quickly, while the Config Server is starting up.
With <code class="literal">cloneOnStart</code> not enabled for a configuration source, the Config Server may start successfully with a misconfigured or invalid configuration source and not detect an error until an application requests configuration from that configuration source.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_authentication" href="#_authentication"></a>Authentication</h4></div></div></div><p>To use HTTP basic authentication on the remote repository, add the <code class="literal">username</code> and <code class="literal">password</code> properties separately (not in the URL), as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username</span>: trolley
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password</span>: strongpassword</pre><p>If you do not use HTTPS and user credentials, SSH should also work out of the box when you store keys in the default directories (<code class="literal">~/.ssh</code>) and the URI points to an SSH location, such as <code class="literal">git@github.com:configuration/cloud-configuration</code>.
It is important that an entry for the Git server be present in the <code class="literal">~/.ssh/known_hosts</code> file and that it is in <code class="literal">ssh-rsa</code> format.
Other formats (such as <code class="literal">ecdsa-sha2-nistp256</code>) are not supported.
To avoid surprises, you should ensure that only one entry is present in the <code class="literal">known_hosts</code> file for the Git server and that it matches the URL you provided to the config server.
If you use a hostname in the URL, you want to have exactly that (not the IP) in the <code class="literal">known_hosts</code> file.
The repository is accessed by using JGit, so any documentation you find on that should be applicable.
HTTPS proxy settings can be set in <code class="literal">~/.git/config</code> or (in the same way as for any other JVM process) with
system properties (<code class="literal">-Dhttps.proxyHost</code> and <code class="literal">-Dhttps.proxyPort</code>).</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>If you do not know where your <code class="literal">~/.git</code> directory is, use <code class="literal">git config --global</code> to manipulate the settings (for example, <code class="literal">git config --global http.sslVerify false</code>).</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_authentication_with_aws_codecommit" href="#_authentication_with_aws_codecommit"></a>Authentication with AWS CodeCommit</h4></div></div></div><p>Spring Cloud Config Server also supports <a class="link" href="http://docs.aws.amazon.com/codecommit/latest/userguide/welcome.html" target="_top">AWS CodeCommit</a> authentication.
AWS CodeCommit uses an authentication helper when using Git from the command line.
This helper is not used with the JGit library, so a JGit CredentialProvider for AWS CodeCommit is created if the Git URI matches the AWS CodeCommit pattern.
AWS CodeCommit URIs follow this pattern://git-codecommit.${AWS_REGION}.amazonaws.com/${repopath}.</p><p>If you provide a username and password with an AWS CodeCommit URI, they must be the <a class="link" href="http://docs.aws.amazon.com/AWSSimpleQueueService/latest/SQSGettingStartedGuide/AWSCredentials.html" target="_top">AWS accessKeyId and secretAccessKey</a> that provide access to the repository.
If you do not specify a username and password, the accessKeyId and secretAccessKey are retrieved by using the <a class="link" href="http://docs.aws.amazon.com/sdk-for-java/v1/developer-guide/credentials.html" target="_top">AWS Default Credential Provider Chain</a>.</p><p>If your Git URI matches the CodeCommit URI pattern (shown earlier), you must provide valid AWS credentials in the username and password or in one of the locations supported by the default credential provider chain.
AWS EC2 instances may use <a class="link" href="http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/iam-roles-for-amazon-ec2.html" target="_top">IAM Roles for EC2 Instances</a>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">aws-java-sdk-core</code> jar is an optional dependency.
If the <code class="literal">aws-java-sdk-core</code> jar is not on your classpath, the AWS Code Commit credential provider is not created, regardless of the git server URI.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_git_ssh_configuration_using_properties" href="#_git_ssh_configuration_using_properties"></a>Git SSH configuration using properties</h4></div></div></div><p>By default, the JGit library used by Spring Cloud Config Server uses SSH configuration files such as <code class="literal">~/.ssh/known_hosts</code> and <code class="literal">/etc/ssh/ssh_config</code> when connecting to Git repositories by using an SSH URI.
In cloud environments such as Cloud Foundry, the local filesystem may be ephemeral or not easily accessible.
For those cases, SSH configuration can be set by using Java properties.
In order to activate property-based SSH configuration, the <code class="literal">spring.cloud.config.server.git.ignoreLocalSshSettings</code> property must be set to <code class="literal">true</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: git<em><span class="hl-annotation" style="color: gray">@gitserver.com:team/repo1.git</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ignoreLocalSshSettings</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> hostKey</span>: someHostKey
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> hostKeyAlgorithm</span>: ssh-rsa
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> privateKey</span>: |
-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX
IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF
ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud
<span class="hl-number">1l</span>7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i
oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W
DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd
fY6yTiKxFzwb38IQP0ojIUWNrq0+<span class="hl-number">9</span>Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b
BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG
EmfA6tHV8/<span class="hl-number">4</span>a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj
<span class="hl-number">5</span>MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8
+AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/<span class="hl-number">28</span>I4BX/mOSe
pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG
ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ
xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW
dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi
PhKpeaeIiAaNnFo8m9aoTKr+<span class="hl-number">7</span>I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX
VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z
FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+<span class="hl-number">8</span>wBm+R
gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4
VAykcNgyDvtAVODP+<span class="hl-number">4</span>m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV
cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee
KTbTjefRFhVUjQqnucAvfGi29f+<span class="hl-number">9</span>oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/<span class="hl-number">3</span>gZ38N
CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs
q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J
<span class="hl-number">69</span>pcVH/<span class="hl-number">4</span>rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT
-----END RSA PRIVATE KEY-----</pre><p>The following table describes the SSH configuration properties.</p><div class="table"><a name="d0e1863" href="#d0e1863"></a><p class="title"><b>Table&nbsp;5.1.&nbsp;SSH Configuration Properties</b></p><div class="table-contents"><table summary="SSH Configuration Properties" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; border-left: 0.5pt solid ; border-right: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Property Name</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Remarks</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>ignoreLocalSshSettings</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If <code class="literal">true</code>, use property-based instead of file-based SSH config. Must be set at as <code class="literal">spring.cloud.config.server.git.ignoreLocalSshSettings</code>, <span class="strong"><strong>not</strong></span> inside a repository definition.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>privateKey</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Valid SSH private key. Must be set if <code class="literal">ignoreLocalSshSettings</code> is true and Git URI is SSH format.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>hostKey</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Valid SSH host key. Must be set if <code class="literal">hostKeyAlgorithm</code> is also set.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>hostKeyAlgorithm</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>One of <code class="literal">ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp521</code>. Must be set if <code class="literal">hostKey</code> is also set.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>strictHostKeyChecking</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">true</code> or <code class="literal">false</code>. If false, ignore errors with host key.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>knownHostsFile</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Location of custom <code class="literal">.known_hosts</code> file.</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>preferredAuthentications</strong></span></p></td><td style="" align="left" valign="top"><p>Override server authentication method order. This should allow for evading login prompts if server has keyboard-interactive authentication before the <code class="literal">publickey</code> method.</p></td></tr></tbody></table></div></div><br class="table-break"></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_placeholders_in_git_search_paths" href="#_placeholders_in_git_search_paths"></a>Placeholders in Git Search Paths</h4></div></div></div><p>Spring Cloud Config Server also supports a search path with placeholders for the <code class="literal">{application}</code> and <code class="literal">{profile}</code> (and <code class="literal">{label}</code> if
you need it), as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> searchPaths</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{application}'</span></pre><p>The preceding listing causes a search of the repository for files in the same name as the directory (as well as the top level).
Wildcards are also valid in a search path with placeholders (any matching directory is included in the search).</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_force_pull_in_git_repositories" href="#_force_pull_in_git_repositories"></a>Force pull in Git Repositories</h4></div></div></div><p>As mentioned earlier, Spring Cloud Config Server makes a clone of the remote git repository in case the local copy gets dirty (for example,
folder content changes by an OS process) such that Spring Cloud Config Server cannot update the local copy from remote repository.</p><p>To solve this issue, there is a <code class="literal">force-pull</code> property that makes Spring Cloud Config Server force pull from the remote repository if the local copy is dirty, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> force-pull</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre><p>If you have a multiple-repositories configuration, you can configure the <code class="literal">force-pull</code> property per repository, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://git/common/config-repo.git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> force-pull</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repos</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> team-a</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>: team-a-*
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://git/team-a/config-repo.git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> force-pull</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> team-b</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>: team-b-*
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://git/team-b/config-repo.git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> force-pull</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> team-c</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pattern</span>: team-c-*
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://git/team-a/config-repo.git</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The default value for <code class="literal">force-pull</code> property is <code class="literal">false</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_deleting_untracked_branches_in_git_repositories" href="#_deleting_untracked_branches_in_git_repositories"></a>Deleting untracked branches in Git Repositories</h4></div></div></div><p>As Spring Cloud Config Server has a clone of the remote git repository
after check-outing branch to local repo (e.g fetching properties by label) it will keep this branch
forever or till the next server restart (which creates new local repo).
So there could be a case when remote branch is deleted but local copy of it is still available for fetching.
And if Spring Cloud Config Server client service starts with <code class="literal">--spring.cloud.config.label=deletedRemoteBranch,master</code>
it will fetch properties from <code class="literal">deletedRemoteBranch</code> local branch, but not from <code class="literal">master</code>.</p><p>In order to keep local repository branches clean and up to remote - <code class="literal">deleteUntrackedBranches</code> property could be set.
It will make Spring Cloud Config Server <span class="strong"><strong>force</strong></span> delete untracked branches from local repository.
Example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> deleteUntrackedBranches</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The default value for <code class="literal">deleteUntrackedBranches</code> property is <code class="literal">false</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_git_refresh_rate" href="#_git_refresh_rate"></a>Git Refresh Rate</h4></div></div></div><p>You can control how often the config server will fetch updated configuration data
from your Git backend by using <code class="literal">spring.cloud.config.server.git.refreshRate</code>. The
value of this property is specified in seconds. By default the value is 0, meaning
the config server will fetch updated configuration from the Git repo every time it
is requested.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_version_control_backend_filesystem_use" href="#_version_control_backend_filesystem_use"></a>5.1.2&nbsp;Version Control Backend Filesystem Use</h3></div></div></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>With VCS-based backends (git, svn), files are checked out or cloned to the local filesystem.
By default, they are put in the system temporary directory with a prefix of <code class="literal">config-repo-</code>.
On linux, for example, it could be <code class="literal">/tmp/config-repo-&lt;randomid&gt;</code>.
Some operating systems <a class="link" href="http://serverfault.com/questions/377348/when-does-tmp-get-cleared/377349#377349" target="_top">routinely clean out</a> temporary directories.
This can lead to unexpected behavior, such as missing properties.
To avoid this problem, change the directory that Config Server uses by setting <code class="literal">spring.cloud.config.server.git.basedir</code> or <code class="literal">spring.cloud.config.server.svn.basedir</code> to a directory that does not reside in the system temp structure.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_file_system_backend" href="#_file_system_backend"></a>5.1.3&nbsp;File System Backend</h3></div></div></div><p>There is also a &#8220;native&#8221; profile in the Config Server that does not use Git but loads the config files from the local classpath or file system (any static URL you want to point to with <code class="literal">spring.cloud.config.server.native.searchLocations</code>).
To use the native profile, launch the Config Server with <code class="literal">spring.profiles.active=native</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Remember to use the <code class="literal">file:</code> prefix for file resources (the default without a prefix is usually the classpath).
As with any Spring Boot configuration, you can embed <code class="literal">${}</code>-style environment placeholders, but remember that absolute paths in Windows require an extra <code class="literal">/</code> (for example, <code class="literal"><a class="link" href="file:///${user.home}/config-repo" target="_top">file:///${user.home}/config-repo</a></code>).</p></td></tr></table></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>The default value of the <code class="literal">searchLocations</code> is identical to a local Spring Boot application (that is, <code class="literal">[classpath:/, classpath:/config,
file:./, file:./config]</code>).
This does not expose the <code class="literal">application.properties</code> from the server to all clients, because any property sources present in the server are removed before being sent to the client.</p></td></tr></table></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>A filesystem backend is great for getting started quickly and for testing.
To use it in production, you need to be sure that the file system is reliable and shared across all instances of the Config Server.</p></td></tr></table></div><p>The search locations can contain placeholders for <code class="literal">{application}</code>, <code class="literal">{profile}</code>, and <code class="literal">{label}</code>.
In this way, you can segregate the directories in the path and choose a strategy that makes sense for you (such as subdirectory per application or subdirectory per profile).</p><p>If you do not use placeholders in the search locations, this repository also appends the <code class="literal">{label}</code> parameter of the HTTP resource to a suffix on the search path, so properties files are loaded from each search location <span class="strong"><strong>and</strong></span> a subdirectory with the same name as the label (the labelled properties take precedence in the Spring Environment).
Thus, the default behaviour with no placeholders is the same as adding a search location ending with <code class="literal">/{label}/</code>.
For example, <code class="literal">file:/tmp/config</code> is the same as <code class="literal">file:/tmp/config,file:/tmp/config/{label}</code>.
This behavior can be disabled by setting <code class="literal">spring.cloud.config.server.native.addLabelLocations=false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="vault-backend" href="#vault-backend"></a>5.1.4&nbsp;Vault Backend</h3></div></div></div><p>Spring Cloud Config Server also supports <a class="link" href="https://www.vaultproject.io" target="_top">Vault</a> as a backend.</p><div class="sidebar"><div class="titlepage"></div><p>Vault is a tool for securely accessing secrets.
A secret is anything that to which you want to tightly control access, such as API keys, passwords, certificates, and other sensitive information. Vault provides a unified interface to any secret while providing tight access control and recording a detailed audit log.</p></div><p>For more information on Vault, see the <a class="link" href="https://learn.hashicorp.com/vault/?track=getting-started#getting-started" target="_top">Vault quick start guide</a>.</p><p>To enable the config server to use a Vault backend, you can run your config server with the <code class="literal">vault</code> profile.
For example, in your config server&#8217;s <code class="literal">application.properties</code>, you can add <code class="literal">spring.profiles.active=vault</code>.</p><p>By default, the config server assumes that your Vault server runs at <code class="literal"><a class="link" href="http://127.0.0.1:8200" target="_top">http://127.0.0.1:8200</a></code>.
It also assumes that the name of backend is <code class="literal">secret</code> and the key is <code class="literal">application</code>.
All of these defaults can be configured in your config server&#8217;s <code class="literal">application.properties</code>.
The following table describes configurable Vault properties:</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; border-left: 0.5pt solid ; border-right: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Name</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Default Value</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>host</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>127.0.0.1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>8200</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>scheme</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>http</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>secret</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>defaultKey</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>profileSeparator</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>,</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>kvVersion</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>skipSslValidation</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>timeout</p></td><td style="" align="left" valign="top"><p>5</p></td></tr></tbody></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>All of the properties in the preceding table must be prefixed with <code class="literal">spring.cloud.config.server.vault</code>.</p></td></tr></table></div><p>All configurable properties can be found in <code class="literal">org.springframework.cloud.config.server.environment.VaultEnvironmentRepository</code>.</p><p>Vault 0.10.0 introduced a versioned key-value backend (k/v backend version 2) that exposes a different API than earlier versions, it now requires a <code class="literal">data/</code> between the mount path and the actual context path and wraps secrets in a <code class="literal">data</code> object. Setting <code class="literal">kvVersion=2</code> will take this into account.</p><p>With your config server running, you can make HTTP requests to the server to retrieve
values from the Vault backend.
To do so, you need a token for your Vault server.</p><p>First, place some data in you Vault, as shown in the following example:</p><pre class="programlisting">$ vault kv put secret/application foo=bar baz=bam
$ vault kv put secret/myapp foo=myappsbar</pre><p>Second, make an HTTP request to your config server to retrieve the values, as shown in the following example:</p><p><code class="literal">$ curl -X "GET" "http://localhost:8888/myapp/default" -H "X-Config-Token: yourtoken"</code></p><p>You should see a response similar to the following:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myapp"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"profiles"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"default"</span>
]<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"label"</span>:null<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"version"</span>:null<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"state"</span>:null<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"propertySources"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"vault:myapp"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"source"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myappsbar"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"vault:application"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"source"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"baz"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bam"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_multiple_properties_sources" href="#_multiple_properties_sources"></a>Multiple Properties Sources</h4></div></div></div><p>When using Vault, you can provide your applications with multiple properties sources.
For example, assume you have written data to the following paths in Vault:</p><pre class="programlisting">secret/myApp,dev
secret/myApp
secret/application,dev
secret/application</pre><p>Properties written to <code class="literal">secret/application</code> are available to <a class="link" href="#">all applications using the Config Server</a>.
An application with the name, <code class="literal">myApp</code>, would have any properties written to <code class="literal">secret/myApp</code> and <code class="literal">secret/application</code> available to it.
When <code class="literal">myApp</code> has the <code class="literal">dev</code> profile enabled, properties written to all of the above paths would be available to it, with properties in the first path in the list taking priority over the others.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_accessing_backends_through_a_proxy" href="#_accessing_backends_through_a_proxy"></a>5.1.5&nbsp;Accessing Backends Through a Proxy</h3></div></div></div><p>The configuration server can access a Git or Vault backend through an HTTP or HTTPS proxy. This behavior is controlled for either Git or Vault by settings under <code class="literal">proxy.http</code> and <code class="literal">proxy.https</code>. These settings are per repository, so if you are using a <a class="link" href="#composite-environment-repositories" title="5.1.8&nbsp;Composite Environment Repositories">composite environment repository</a> you must configure proxy settings for each backend in the composite individually. If using a network which requires separate proxy servers for HTTP and HTTPS URLs, you can configure both the HTTP and the HTTPS proxy settings for a single backend.</p><p>The following table describes the proxy configuration properties for both HTTP and HTTPS proxies. All of these properties must be prefixed by <code class="literal">proxy.http</code> or <code class="literal">proxy.https</code>.</p><div class="table"><a name="d0e2350" href="#d0e2350"></a><p class="title"><b>Table&nbsp;5.2.&nbsp;Proxy Configuration Properties</b></p><div class="table-contents"><table summary="Proxy Configuration Properties" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; border-left: 0.5pt solid ; border-right: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Property Name</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Remarks</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>host</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The host of the proxy.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>port</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The port with which to access the proxy.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>nonProxyHosts</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Any hosts which the configuration server should access outside the proxy. If values are provided for both <code class="literal">proxy.http.nonProxyHosts</code> and <code class="literal">proxy.https.nonProxyHosts</code>, the <code class="literal">proxy.http</code> value will be used.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>username</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The username with which to authenticate to the proxy. If values are provided for both <code class="literal">proxy.http.username</code> and <code class="literal">proxy.https.username</code>, the <code class="literal">proxy.http</code> value will be used.</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>password</strong></span></p></td><td style="" align="left" valign="top"><p>The password with which to authenticate to the proxy. If values are provided for both <code class="literal">proxy.http.password</code> and <code class="literal">proxy.https.password</code>, the <code class="literal">proxy.http</code> value will be used.</p></td></tr></tbody></table></div></div><br class="table-break"><p>The following configuration uses an HTTPS proxy to access a Git repository.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> active</span>: git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://github.com/spring-cloud-samples/config-repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> proxy</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> https</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> host</span>: my-proxy.host.io
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password</span>: myproxypassword
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'3128'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username</span>: myproxyusername
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> nonProxyHosts</span>: example.com</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_sharing_configuration_with_all_applications" href="#_sharing_configuration_with_all_applications"></a>5.1.6&nbsp;Sharing Configuration With All Applications</h3></div></div></div><p>Sharing configuration between all applications varies according to which approach you take, as described in the following topics:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#spring-cloud-config-server-file-based-repositories" title="File Based Repositories">the section called &#8220;File Based Repositories&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-config-server-vault-server" title="Vault Server">the section called &#8220;Vault Server&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-config-server-file-based-repositories" href="#spring-cloud-config-server-file-based-repositories"></a>File Based Repositories</h4></div></div></div><p>With file-based (git, svn, and native) repositories, resources with file names in <code class="literal">application*</code> (<code class="literal">application.properties</code>, <code class="literal">application.yml</code>, <code class="literal">application-*.properties</code>, and so on) are shared between all client applications.
You can use resources with these file names to configure global defaults and have them be overridden by application-specific files as necessary.</p><p>The #_property_overrides[property overrides] feature can also be used for setting global defaults, with placeholders applications
allowed to override them locally.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>With the &#8220;native&#8221; profile (a local file system backend) , you should use an explicit search location that is not part of the server&#8217;s own configuration.
Otherwise, the <code class="literal">application*</code> resources in the default search locations get removed because they are part of the server.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-config-server-vault-server" href="#spring-cloud-config-server-vault-server"></a>Vault Server</h4></div></div></div><p>When using Vault as a backend, you can share configuration with all applications by placing configuration in <code class="literal">secret/application</code>.
For example, if you run the following Vault command, all applications using the config server will have the properties <code class="literal">foo</code> and <code class="literal">baz</code> available to them:</p><pre class="programlisting">$ vault write secret/application foo=bar baz=bam</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_jdbc_backend" href="#_jdbc_backend"></a>5.1.7&nbsp;JDBC Backend</h3></div></div></div><p>Spring Cloud Config Server supports JDBC (relational database) as a backend for configuration properties.
You can enable this feature by adding <code class="literal">spring-jdbc</code> to the classpath and using the <code class="literal">jdbc</code> profile or by adding a bean of type <code class="literal">JdbcEnvironmentRepository</code>.
If you include the right dependencies on the classpath (see the user guide for more details on that), Spring Boot configures a data source.</p><p>The database needs to have a table called <code class="literal">PROPERTIES</code> with columns called <code class="literal">APPLICATION</code>, <code class="literal">PROFILE</code>, and <code class="literal">LABEL</code> (with the usual <code class="literal">Environment</code> meaning), plus <code class="literal">KEY</code> and <code class="literal">VALUE</code> for the key and value pairs in <code class="literal">Properties</code> style.
All fields are of type String in Java, so you can make them <code class="literal">VARCHAR</code> of whatever length you need.
Property values behave in the same way as they would if they came from Spring Boot properties files named <code class="literal">{application}-{profile}.properties</code>, including all the encryption and decryption, which will be applied as post-processing steps (that is, not in the repository implementation directly).</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="composite-environment-repositories" href="#composite-environment-repositories"></a>5.1.8&nbsp;Composite Environment Repositories</h3></div></div></div><p>In some scenarios, you may wish to pull configuration data from multiple environment repositories.
To do so, you can enable the <code class="literal">composite</code> profile in your configuration server&#8217;s application properties or YAML file.
If, for example, you want to pull configuration data from a Subversion repository as well as two Git repositories, you can set the following properties for your configuration server:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> active</span>: composite
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> composite</span>:
-
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> type</span>: svn
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: file:///path/to/svn/repo
-
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> type</span>: git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: file:///path/to/rex/git/repo
-
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> type</span>: git
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: file:///path/to/walter/git/repo</pre><p>Using this configuration, precedence is determined by the order in which repositories are listed under the <code class="literal">composite</code> key.
In the above example, the Subversion repository is listed first, so a value found in the Subversion repository will override values found for the same property in one of the Git repositories.
A value found in the <code class="literal">rex</code> Git repository will be used before a value found for the same property in the <code class="literal">walter</code> Git repository.</p><p>If you want to pull configuration data only from repositories that are each of distinct types, you can enable the corresponding profiles, rather than the <code class="literal">composite</code> profile, in your configuration server&#8217;s application properties or YAML file.
If, for example, you want to pull configuration data from a single Git repository and a single HashiCorp Vault server, you can set the following properties for your configuration server:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> active</span>: git<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> vault
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> git</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: file:///path/to/git/repo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> order</span>: <span class="hl-number">2</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> host</span>: <span class="hl-number">127.0</span>.<span class="hl-number">0.1</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">8200</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> order</span>: <span class="hl-number">1</span></pre><p>Using this configuration, precedence can be determined by an <code class="literal">order</code> property.
You can use the <code class="literal">order</code> property to specify the priority order for all your repositories.
The lower the numerical value of the <code class="literal">order</code> property, the higher priority it has.
The priority order of a repository helps resolve any potential conflicts between repositories that contain values for the same properties.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If your composite environment includes a Vault server as in the previous example, you must include a Vault token in every request made to the configuration server. See <a class="link" href="#vault-backend" title="5.1.4&nbsp;Vault Backend">Vault Backend</a>.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Any type of failure when retrieving values from an environment repository results in a failure for the entire composite environment.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When using a composite environment, it is important that all repositories contain the same labels.
If you have an environment similar to those in the preceding examples and you request configuration data with the <code class="literal">master</code> label but the Subversion repository does not contain a branch called <code class="literal">master</code>, the entire request fails.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_custom_composite_environment_repositories" href="#_custom_composite_environment_repositories"></a>Custom Composite Environment Repositories</h4></div></div></div><p>In addition to using one of the environment repositories from Spring Cloud, you can also provide your own <code class="literal">EnvironmentRepository</code> bean to be included as part of a composite environment.
To do so, your bean must implement the <code class="literal">EnvironmentRepository</code> interface.
If you want to control the priority of your custom <code class="literal">EnvironmentRepository</code> within the composite environment, you should also implement the <code class="literal">Ordered</code> interface and override the <code class="literal">getOrdered</code> method.
If you do not implement the <code class="literal">Ordered</code> interface, your <code class="literal">EnvironmentRepository</code> is given the lowest priority.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_property_overrides" href="#_property_overrides"></a>5.1.9&nbsp;Property Overrides</h3></div></div></div><p>The Config Server has an &#8220;overrides&#8221; feature that lets the operator provide configuration properties to all applications.
The overridden properties cannot be accidentally changed by the application with the normal Spring Boot hooks.
To declare overrides, add a map of name-value pairs to <code class="literal">spring.cloud.config.server.overrides</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> overrides</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> foo</span>: bar</pre><p>The preceding examples causes all applications that are config clients to read <code class="literal">foo=bar</code>, independent of their own configuration.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>A configuration system cannot force an application to use configuration data in any particular way.
Consequently, overrides are not enforceable.
However, they do provide useful default behavior for Spring Cloud Config clients.</p></td></tr></table></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Normally, Spring environment placeholders with <code class="literal">${}</code> can be escaped (and resolved on the client) by using backslash (<code class="literal">\</code>) to escape the <code class="literal">$</code> or the <code class="literal">{</code>.
For example, <code class="literal">\${app.foo:bar}</code> resolves to <code class="literal">bar</code>, unless the app provides its own <code class="literal">app.foo</code>.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>In YAML, you do not need to escape the backslash itself.
However, in properties files, you do need to escape the backslash, when you configure the overrides on the server.</p></td></tr></table></div><p>You can change the priority of all overrides in the client to be more like default values, letting applications supply their own values in environment variables or System properties, by setting the <code class="literal">spring.cloud.config.overrideNone=true</code> flag (the default is false) in the remote repository.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_health_indicator_2" href="#_health_indicator_2"></a>5.2&nbsp;Health Indicator</h2></div></div></div><p>Config Server comes with a Health Indicator that checks whether the configured <code class="literal">EnvironmentRepository</code> is working.
By default, it asks the <code class="literal">EnvironmentRepository</code> for an application named <code class="literal">app</code>, the <code class="literal">default</code> profile, and the default label provided by the <code class="literal">EnvironmentRepository</code> implementation.</p><p>You can configure the Health Indicator to check more applications along with custom profiles and custom labels, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> health</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repositories</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> myservice</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> label</span>: mylabel
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> myservice-dev</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: myservice
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>: development</pre><p>You can disable the Health Indicator by setting <code class="literal">spring.cloud.config.server.health.enabled=false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_security" href="#_security"></a>5.3&nbsp;Security</h2></div></div></div><p>You can secure your Config Server in any way that makes sense to you (from physical network security to OAuth2 bearer tokens), because Spring Security and Spring Boot offer support for many security arrangements.</p><p>To use the default Spring Boot-configured HTTP Basic security, include Spring Security on the classpath (for example, through <code class="literal">spring-boot-starter-security</code>).
The default is a username of <code class="literal">user</code> and a randomly generated password. A random password is not useful in practice, so we recommend you configure the password (by setting <code class="literal">spring.security.user.password</code>) and encrypt it (see below for instructions on how to do that).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_encryption_and_decryption_2" href="#_encryption_and_decryption_2"></a>5.4&nbsp;Encryption and Decryption</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>To use the encryption and decryption features you need the full-strength JCE installed in your JVM (it is not included by default).
You can download the &#8220;Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files&#8221; from Oracle and follow the installation instructions (essentially, you need to replace the two policy files in the JRE lib/security directory with the ones that you downloaded).</p></td></tr></table></div><p>If the remote property sources contain encrypted content (values starting with <code class="literal">{cipher}</code>), they are decrypted before sending to clients over HTTP.
The main advantage of this setup is that the property values need not be in plain text when they are &#8220;at rest&#8221; (for example, in a git repository).
If a value cannot be decrypted, it is removed from the property source and an additional property is added with the same key but prefixed with <code class="literal">invalid</code> and a value that means &#8220;not applicable&#8221; (usually <code class="literal">&lt;n/a&gt;</code>).
This is largely to prevent cipher text being used as a password and accidentally leaking.</p><p>If you set up a remote config repository for config client applications, it might contain an <code class="literal">application.yml</code> similar to the following:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> datasource</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username</span>: dbuser
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'</span></pre><p>
</p><p>Encrypted values in a .properties file must not be wrapped in quotes. Otherwise, the value is not decrypted. The following example shows values that would work:</p><p><b>application.properties.&nbsp;</b>
</p><pre class="screen">spring.datasource.username: dbuser
spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ</pre><p>
</p><p>You can safely push this plain text to a shared git repository, and the secret password remains protected.</p><p>The server also exposes <code class="literal">/encrypt</code> and <code class="literal">/decrypt</code> endpoints (on the assumption that these are secured and only accessed by authorized agents).
If you edit a remote config file, you can use the Config Server to encrypt values by POSTing to the <code class="literal">/encrypt</code> endpoint, as shown in the following example:</p><pre class="screen">$ curl localhost:8888/encrypt -d mysecret
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If the value you encrypt has characters in it that need to be URL encoded, you should use the <code class="literal">--data-urlencode</code> option to <code class="literal">curl</code> to make sure they are encoded properly.</p></td></tr></table></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Be sure not to include any of the curl command statistics in the encrypted value.
Outputting the value to a file can help avoid this problem.</p></td></tr></table></div><p>The inverse operation is also available through <code class="literal">/decrypt</code> (provided the server is
configured with a symmetric key or a full key pair), as shown in the following example:</p><pre class="screen">$ curl localhost:8888/decrypt -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>If you testing with curl, then use <code class="literal">--data-urlencode</code> (instead of <code class="literal">-d</code>) or set an explicit <code class="literal">Content-Type: text/plain</code> to make sure curl encodes the data correctly when there are special characters ('+' is particularly tricky).</p></td></tr></table></div><p>Take the encrypted value and add the <code class="literal">{cipher}</code> prefix before you put it in the YAML or properties file and before you commit and push it to a remote (potentially insecure) store.</p><p>The <code class="literal">/encrypt</code> and <code class="literal">/decrypt</code> endpoints also both accept paths in the form of <code class="literal">/*/{name}/{profiles}</code>, which can be used to control cryptography on a per-application (name) and per-profile basis when clients call into the main environment resource.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>To control the cryptography in this granular way, you must also provide a <code class="literal">@Bean</code> of type <code class="literal">TextEncryptorLocator</code> that creates a different encryptor per name and profiles.
The one that is provided by default does not do so (all encryptions use the same key).</p></td></tr></table></div><p>The <code class="literal">spring</code> command line client (with Spring Cloud CLI extensions
installed) can also be used to encrypt and decrypt, as shown in the following example:</p><pre class="screen">$ spring encrypt mysecret --key foo
682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
$ spring decrypt --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
mysecret</pre><p>To use a key in a file (such as an RSA public key for encryption), prepend
the key value with "@" and provide the file path, as shown in the following example:</p><pre class="screen">$ spring encrypt mysecret --key @${HOME}/.ssh/id_rsa.pub
AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">--key</code> argument is mandatory (despite having a <code class="literal">--</code> prefix).</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_key_management" href="#_key_management"></a>5.5&nbsp;Key Management</h2></div></div></div><p>The Config Server can use a symmetric (shared) key or an asymmetric one (RSA key pair).
The asymmetric choice is superior in terms of security, but it is often more convenient to use a symmetric key since it is a single property value to configure in the <code class="literal">bootstrap.properties</code>.</p><p>To configure a symmetric key, you need to set <code class="literal">encrypt.key</code> to a secret String (or use the <code class="literal">ENCRYPT_KEY</code> environment variable to keep it out of plain-text configuration files).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>You cannot configure an asymmetric key using <code class="literal">encrypt.key</code>.</p></td></tr></table></div><p>To configure an asymmetric key use a keystore (e.g. as
created by the <code class="literal">keytool</code> utility that comes with the JDK). The
keystore properties are <code class="literal">encrypt.keyStore.*</code> with <code class="literal">*</code> equal to</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; border-left: 0.5pt solid ; border-right: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="center" valign="top">Property</th><th style="border-bottom: 0.5pt solid ; " align="center" valign="top">Description</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">encrypt.keyStore.location</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Contains a <code class="literal">Resource</code> location</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">encrypt.keyStore.password</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Holds the password that unlocks the keystore</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">encrypt.keyStore.alias</code></p></td><td style="" align="left" valign="top"><p>Identifies which key in the store to use</p></td></tr></tbody></table></div><p>The encryption is done with the public key, and a private key is
needed for decryption.
Thus, in principle, you can configure only the public key in the server if you want to only encrypt (and are prepared to decrypt the values yourself locally with the private key).
In practice, you might not want to do decrypt locally, because it spreads the key management process around all the clients, instead of
concentrating it in the server.
On the other hand, it can be a useful option if your config server is relatively insecure and only a handful of clients need the encrypted properties.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_creating_a_key_store_for_testing" href="#_creating_a_key_store_for_testing"></a>5.6&nbsp;Creating a Key Store for Testing</h2></div></div></div><p>To create a keystore for testing, you can use a command resembling the following:</p><pre class="screen">$ keytool -genkeypair -alias mytestkey -keyalg RSA \
-dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" \
-keypass changeme -keystore server.jks -storepass letmein</pre><p>Put the <code class="literal">server.jks</code> file in the classpath (for instance) and then, in
your <code class="literal">bootstrap.yml</code>, for the Config Server, create the following settings:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">encrypt</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> keyStore</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> location</span>: classpath:/server.jks
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password</span>: letmein
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> alias</span>: mytestkey
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> secret</span>: changeme</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_multiple_keys_and_key_rotation" href="#_using_multiple_keys_and_key_rotation"></a>5.7&nbsp;Using Multiple Keys and Key Rotation</h2></div></div></div><p>In addition to the <code class="literal">{cipher}</code> prefix in encrypted property values, the Config Server looks for zero or more <code class="literal">{name:value}</code> prefixes before the start of the (Base64 encoded) cipher text.
The keys are passed to a <code class="literal">TextEncryptorLocator</code>, which can do whatever logic it needs to locate a <code class="literal">TextEncryptor</code> for the cipher.
If you have configured a keystore (<code class="literal">encrypt.keystore.location</code>), the default locator looks for keys with aliases supplied by the <code class="literal">key</code> prefix, with a cipher text like resembling the following:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">foo</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bar</span>: `{cipher}{key:testkey}...`</pre><p>The locator looks for a key named "testkey".
A secret can also be supplied by using a <code class="literal">{secret:&#8230;&#8203;}</code> value in the prefix.
However, if it is not supplied, the default is to use the keystore password (which is what you get when you build a keystore and do not specify a secret).
If you do supply a secret, you should also encrypt the secret using a custom <code class="literal">SecretLocator</code>.</p><p>When the keys are being used only to encrypt a few bytes of configuration data (that is, they are not being used elsewhere), key rotation is hardly ever necessary on cryptographic grounds.
However, you might occasionally need to change the keys (for example, in the event of a security breach).
In that case, all the clients would need to change their source config files (for example, in git) and use a new <code class="literal">{key:&#8230;&#8203;}</code> prefix in all the ciphers.
Note that the clients need to first check that the key alias is available in the Config Server keystore.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>If you want to let the Config Server handle all encryption as well as decryption, the <code class="literal">{name:value}</code> prefixes can also be added as plain text posted to the <code class="literal">/encrypt</code> endpoint, .</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_serving_encrypted_properties" href="#_serving_encrypted_properties"></a>5.8&nbsp;Serving Encrypted Properties</h2></div></div></div><p>Sometimes you want the clients to decrypt the configuration locally, instead of doing it in the server.
In that case, if you provide the <code class="literal">encrypt.*</code> configuration to locate a key, you can still have <code class="literal">/encrypt</code> and <code class="literal">/decrypt</code> endpoints, but you need to explicitly switch off the decryption of outgoing properties by placing <code class="literal">spring.cloud.config.server.encrypt.enabled=false</code> in <code class="literal">bootstrap.[yml|properties]</code>.
If you do not care about the endpoints, it should work if you do not configure either the key or the enabled flag.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_serving_alternative_formats" href="#_serving_alternative_formats"></a>6.&nbsp;Serving Alternative Formats</h2></div></div></div><p>The default JSON format from the environment endpoints is perfect for consumption by Spring applications, because it maps directly onto the <code class="literal">Environment</code> abstraction.
If you prefer, you can consume the same data as YAML or Java properties by adding a suffix (".yml", ".yaml" or ".properties") to the resource path.
This can be useful for consumption by applications that do not care about the structure of the JSON endpoints or the extra metadata they provide (for example, an application that is not using Spring might benefit from the simplicity of this approach).</p><p>The YAML and properties representations have an additional flag (provided as a boolean query parameter called <code class="literal">resolvePlaceholders</code>) to signal that placeholders in the source documents (in the standard Spring <code class="literal">${&#8230;&#8203;}</code> form) should be resolved in the output before rendering, where possible.
This is a useful feature for consumers that do not know about the Spring placeholder conventions.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>There are limitations in using the YAML or properties formats, mainly in relation to the loss of metadata.
For example, the JSON is structured as an ordered list of property sources, with names that correlate with the source.
The YAML and properties forms are coalesced into a single map, even if the origin of the values has multiple sources, and the names of the original source files are lost.
Also, the YAML representation is not necessarily a faithful representation of the YAML source in a backing repository either. It is constructed from a list of flat property sources, and assumptions have to be made about the form of the keys.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_serving_plain_text" href="#_serving_plain_text"></a>7.&nbsp;Serving Plain Text</h2></div></div></div><p>Instead of using the <code class="literal">Environment</code> abstraction (or one of the alternative representations of it in YAML or properties format), your applications might need generic plain-text configuration files that are tailored to their environment.
The Config Server provides these through an additional endpoint at <code class="literal">/{name}/{profile}/{label}/{path}</code>, where <code class="literal">name</code>, <code class="literal">profile</code>, and <code class="literal">label</code> have the same meaning as the regular environment endpoint, but <code class="literal">path</code> is a file name (such as <code class="literal">log.xml</code>).
The source files for this endpoint are located in the same way as for the environment endpoints.
The same search path is used for properties and YAML files.
However, instead of aggregating all matching resources, only the first one to match is returned.</p><p>After a resource is located, placeholders in the normal format (<code class="literal">${&#8230;&#8203;}</code>) are resolved by using the effective <code class="literal">Environment</code> for the supplied application name, profile, and label.
In this way, the resource endpoint is tightly integrated with the environment endpoints.
Consider the following example for a GIT or SVN repository:</p><pre class="screen">application.yml
nginx.conf</pre><p>where <code class="literal">nginx.conf</code> looks like this:</p><pre class="screen">server {
listen 80;
server_name ${nginx.server.name};
}</pre><p>and <code class="literal">application.yml</code> like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">nginx</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: example.com
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">---</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>: development
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">nginx</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: develop.com</pre><p>The <code class="literal">/foo/default/master/nginx.conf</code> resource might be as follows:</p><pre class="screen">server {
listen 80;
server_name example.com;
}</pre><p>and <code class="literal">/foo/development/master/nginx.conf</code> like this:</p><pre class="screen">server {
listen 80;
server_name develop.com;
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>As with the source files for environment configuration, the <code class="literal">profile</code> is used to resolve the file name.
So, if you want a profile-specific file, <code class="literal">/*/development/*/logback.xml</code> can be resolved by a file called <code class="literal">logback-development.xml</code> (in preference to <code class="literal">logback.xml</code>).</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you do not want to supply the <code class="literal">label</code> and let the server use the default label, you can supply a <code class="literal">useDefaultLabel</code> request parameter.
So, the preceding example for the <code class="literal">default</code> profile could be <code class="literal">/foo/default/nginx.conf?useDefaultLabel</code>.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_embedding_the_config_server" href="#_embedding_the_config_server"></a>8.&nbsp;Embedding the Config Server</h2></div></div></div><p>The Config Server runs best as a standalone application.
However, if need be, you can embed it in another application.
To do so, use the <code class="literal">@EnableConfigServer</code> annotation.
An optional property named <code class="literal">spring.cloud.config.server.bootstrap</code> can be useful in this case.
It is a flag to indicate whether the server should configure itself from its own remote repository.
By default, the flag is off, because it can delay startup.
However, when embedded in another application, it makes sense to initialize the same way as any other application.
When setting <code class="literal">spring.cloud.config.server.bootstrap</code> to <code class="literal">true</code> you must also use a <a class="link" href="#composite-environment-repositories" title="5.1.8&nbsp;Composite Environment Repositories">composite environment repository configuration</a>.
For example</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: configserver
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> active</span>: composite
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> composite</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - type</span>: native
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> search-locations</span>: ${HOME}/Desktop/config
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bootstrap</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you use the bootstrap flag, the config server needs to have its name and repository URI configured in <code class="literal">bootstrap.yml</code>.</p></td></tr></table></div><p>To change the location of the server endpoints, you can (optionally) set <code class="literal">spring.cloud.config.server.prefix</code> (for example, <code class="literal">/config</code>), to serve the resources under a prefix.
The prefix should start but not end with a <code class="literal">/</code>.
It is applied to the <code class="literal">@RequestMappings</code> in the Config Server (that is, underneath the Spring Boot <code class="literal">server.servletPath</code> and <code class="literal">server.contextPath</code> prefixes).</p><p>If you want to read the configuration for an application directly from the backend repository (instead of from the config server), you
basically want an embedded config server with no endpoints.
You can switch off the endpoints entirely by not using the <code class="literal">@EnableConfigServer</code> annotation (set <code class="literal">spring.cloud.config.server.bootstrap=true</code>).</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_push_notifications_and_spring_cloud_bus" href="#_push_notifications_and_spring_cloud_bus"></a>9.&nbsp;Push Notifications and Spring Cloud Bus</h2></div></div></div><p>Many source code repository providers (such as Github, Gitlab, Gitea, Gitee, Gogs, or Bitbucket) notify you of changes in a repository through a webhook.
You can configure the webhook through the provider&#8217;s user interface as a URL and a set of events in which you are interested.
For instance, <a class="link" href="https://developer.github.com/v3/activity/events/types/#pushevent" target="_top">Github</a> uses a POST to the webhook with a JSON body containing a list of commits and a header (<code class="literal">X-Github-Event</code>) set to <code class="literal">push</code>.
If you add a dependency on the <code class="literal">spring-cloud-config-monitor</code> library and activate the Spring Cloud Bus in your Config Server, then a <code class="literal">/monitor</code> endpoint is enabled.</p><p>When the webhook is activated, the Config Server sends a <code class="literal">RefreshRemoteApplicationEvent</code> targeted at the applications it thinks might have changed.
The change detection can be strategized.
However, by default, it looks for changes in files that match the application name (for example, <code class="literal">foo.properties</code> is targeted at the <code class="literal">foo</code> application, while <code class="literal">application.properties</code> is targeted at all applications).
The strategy to use when you want to override the behavior is <code class="literal">PropertyPathNotificationExtractor</code>, which accepts the request headers and body as parameters and returns a list of file paths that changed.</p><p>The default configuration works out of the box with Github, Gitlab, Gitea, Gitee, Gogs or Bitbucket.
In addition to the JSON notifications from Github, Gitlab, Gitee, or Bitbucket, you can trigger a change notification by POSTing to <code class="literal">/monitor</code> with form-encoded body parameters in the pattern of <code class="literal">path={name}</code>.
Doing so broadcasts to applications matching the <code class="literal">{name}</code> pattern (which can contain wildcards).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">RefreshRemoteApplicationEvent</code> is transmitted only if the <code class="literal">spring-cloud-bus</code> is activated in both the Config Server and in the client application.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The default configuration also detects filesystem changes in local git repositories. In that case, the webhook is not used. However, as soon as you edit a config file, a refresh is broadcast.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_config_client" href="#_spring_cloud_config_client"></a>10.&nbsp;Spring Cloud Config Client</h2></div></div></div><p>A Spring Boot application can take immediate advantage of the Spring Config Server (or other external property sources provided by the application developer).
It also picks up some additional useful features related to <code class="literal">Environment</code> change events.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="config-first-bootstrap" href="#config-first-bootstrap"></a>10.1&nbsp;Config First Bootstrap</h2></div></div></div><p>The default behavior for any application that has the Spring Cloud Config Client on the classpath is as follows:
When a config client starts, it binds to the Config Server (through the <code class="literal">spring.cloud.config.uri</code> bootstrap configuration property) and initializes Spring <code class="literal">Environment</code> with remote property sources.</p><p>The net result of this behavior is that all client applications that want to consume the Config Server need a <code class="literal">bootstrap.yml</code> (or an environment variable) with the server address set in <code class="literal">spring.cloud.config.uri</code> (it defaults to "http://localhost:8888").</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="discovery-first-bootstrap" href="#discovery-first-bootstrap"></a>10.2&nbsp;Discovery First Bootstrap</h2></div></div></div><p>If you use a `DiscoveryClient implementation, such as Spring Cloud Netflix and Eureka Service Discovery or Spring Cloud Consul, you can have the Config Server register with the Discovery Service.
However, in the default &#8220;Config First&#8221; mode, clients cannot take advantage of the registration.</p><p>If you prefer to use <code class="literal">DiscoveryClient</code> to locate the Config Server, you can do so by setting <code class="literal">spring.cloud.config.discovery.enabled=true</code> (the default is <code class="literal">false</code>).
The net result of doing so is that client applications all need a <code class="literal">bootstrap.yml</code> (or an environment variable) with the appropriate discovery configuration.
For example, with Spring Cloud Netflix, you need to define the Eureka server address (for example, in <code class="literal">eureka.client.serviceUrl.defaultZone</code>).
The price for using this option is an extra network round trip on startup, to locate the service registration.
The benefit is that, as long as the Discovery Service is a fixed point, the Config Server can change its coordinates.
The default service ID is <code class="literal">configserver</code>, but you can change that on the client by setting <code class="literal">spring.cloud.config.discovery.serviceId</code> (and on the server, in the usual way for a service, such as by setting <code class="literal">spring.application.name</code>).</p><p>The discovery client implementations all support some kind of metadata map (for example, we have <code class="literal">eureka.instance.metadataMap</code> for Eureka).
Some additional properties of the Config Server may need to be configured in its service registration metadata so that clients can connect correctly.
If the Config Server is secured with HTTP Basic, you can configure the credentials as <code class="literal">username</code> and <code class="literal">password</code>.
Also, if the Config Server has a context path, you can set <code class="literal">configPath</code>.
For example, the following YAML file is for a Config Server that is a Eureka client:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">eureka</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> instance</span>:
...
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> metadataMap</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> user</span>: osufhalskjrtl
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password</span>: lviuhlszvaorhvlo5847
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> configPath</span>: /config</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="config-client-fail-fast" href="#config-client-fail-fast"></a>10.3&nbsp;Config Client Fail Fast</h2></div></div></div><p>In some cases, you may want to fail startup of a service if it cannot connect to the Config Server.
If this is the desired behavior, set the bootstrap configuration property <code class="literal">spring.cloud.config.fail-fast=true</code> to make the client halt with an Exception.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="config-client-retry" href="#config-client-retry"></a>10.4&nbsp;Config Client Retry</h2></div></div></div><p>If you expect that the config server may occasionally be unavailable when your application starts, you can make it keep trying after a failure.
First, you need to set <code class="literal">spring.cloud.config.fail-fast=true</code>.
Then you need to add <code class="literal">spring-retry</code> and <code class="literal">spring-boot-starter-aop</code> to your classpath.
The default behavior is to retry six times with an initial backoff interval of 1000ms and an exponential multiplier of 1.1 for subsequent backoffs.
You can configure these properties (and others) by setting the <code class="literal">spring.cloud.config.retry.*</code> configuration properties.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>To take full control of the retry behavior, add a <code class="literal">@Bean</code> of type <code class="literal">RetryOperationsInterceptor</code> with an ID of <code class="literal">configServerRetryInterceptor</code>.
Spring Retry has a <code class="literal">RetryInterceptorBuilder</code> that supports creating one.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_locating_remote_configuration_resources" href="#_locating_remote_configuration_resources"></a>10.5&nbsp;Locating Remote Configuration Resources</h2></div></div></div><p>The Config Service serves property sources from <code class="literal">/{name}/{profile}/{label}</code>, where the default bindings in the client app are as follows:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">"name" = <code class="literal">${spring.application.name}</code></li><li class="listitem">"profile" = <code class="literal">${spring.profiles.active}</code> (actually <code class="literal">Environment.getActiveProfiles()</code>)</li><li class="listitem">"label" = "master"</li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When setting the property <code class="literal">${spring.application.name}</code> do not prefix your app name with the reserved word <code class="literal">application-</code> to prevent issues resolving the correct property source.</p></td></tr></table></div><p>You can override all of them by setting <code class="literal">spring.cloud.config.*</code> (where <code class="literal">*</code> is <code class="literal">name</code>, <code class="literal">profile</code> or <code class="literal">label</code>).
The <code class="literal">label</code> is useful for rolling back to previous versions of configuration.
With the default Config Server implementation, it can be a git label, branch name, or commit ID.
Label can also be provided as a comma-separated list.
In that case, the items in the list are tried one by one until one succeeds.
This behavior can be useful when working on a feature branch.
For instance, you might want to align the config label with your branch but make it optional (in that case, use <code class="literal">spring.cloud.config.label=myfeature,develop</code>).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_specifying_multiple_urls_for_the_config_server" href="#_specifying_multiple_urls_for_the_config_server"></a>10.6&nbsp;Specifying Multiple Urls for the Config Server</h2></div></div></div><p>To ensure high availability when you have multiple instances of Config Server deployed and expect one or more instances to be unavailable from time to time, you can either specify multiple URLs (as a comma-separated list under the <code class="literal">spring.cloud.config.uri</code> property) or have all your instances register in a Service Registry like Eureka ( if using Discovery-First Bootstrap mode ). Note that doing so ensures high availability only when the Config Server is not running (that is, when the application has exited) or when a connection timeout has occurred. For example, if the Config Server returns a 500 (Internal Server Error) response or the Config Client receives a 401 from the Config Server (due to bad credentials or other causes), the Config Client does not try to fetch properties from other URLs. An error of that kind indicates a user issue rather than an availability problem.</p><p>If you use HTTP basic security on your Config Server, it is currently possible to support per-Config Server auth credentials only if you embed the credentials in each URL you specify under the <code class="literal">spring.cloud.config.uri</code> property. If you use any other kind of security mechanism, you cannot (currently) support per-Config Server authentication and authorization.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_configuring_read_timeouts" href="#_configuring_read_timeouts"></a>10.7&nbsp;Configuring Read Timeouts</h2></div></div></div><p>If you want to configure read timeout, this can be done by using the property <code class="literal">spring.cloud.config.request-read-timeout</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_security_2" href="#_security_2"></a>10.8&nbsp;Security</h2></div></div></div><p>If you use HTTP Basic security on the server, clients need to know the password (and username if it is not the default).
You can specify the username and password through the config server URI or via separate username and password properties, as shown in the following example:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://user:secret@myconfig.mycompany.com</pre><p>
</p><p>The following example shows an alternate way to pass the same information:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://myconfig.mycompany.com
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username</span>: user
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password</span>: secret</pre><p>
</p><p>The <code class="literal">spring.cloud.config.password</code> and <code class="literal">spring.cloud.config.username</code> values override anything that is provided in the URI.</p><p>If you deploy your apps on Cloud Foundry, the best way to provide the password is through service credentials (such as in the URI, since it does not need to be in a config file).
The following example works locally and for a user-provided service on Cloud Foundry named <code class="literal">configserver</code>:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: ${vcap.services.configserver.credentials.uri:http://user:password@localhost:<span class="hl-number">8888</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
</p><p>If you use another form of security, you might need to <a class="link" href="#custom-rest-template" title="10.8.2&nbsp;Providing A Custom RestTemplate">provide a <code class="literal">RestTemplate</code></a> to the <code class="literal">ConfigServicePropertySourceLocator</code> (for example, by grabbing it in the bootstrap context and injecting it).</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_health_indicator_3" href="#_health_indicator_3"></a>10.8.1&nbsp;Health Indicator</h3></div></div></div><p>The Config Client supplies a Spring Boot Health Indicator that attempts to load configuration from the Config Server.
The health indicator can be disabled by setting <code class="literal">health.config.enabled=false</code>.
The response is also cached for performance reasons.
The default cache time to live is 5 minutes.
To change that value, set the <code class="literal">health.config.time-to-live</code> property (in milliseconds).</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="custom-rest-template" href="#custom-rest-template"></a>10.8.2&nbsp;Providing A Custom RestTemplate</h3></div></div></div><p>In some cases, you might need to customize the requests made to the config server from the client.
Typically, doing so involves passing special <code class="literal">Authorization</code> headers to authenticate requests to the server.
To provide a custom <code class="literal">RestTemplate</code>:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Create a new configuration bean with an implementation of <code class="literal">PropertySourceLocator</code>, as shown in the following example:</li></ol></div><p><b>CustomConfigServiceBootstrapConfiguration.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomConfigServiceBootstrapConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ConfigServicePropertySourceLocator configServicePropertySourceLocator() {
ConfigClientProperties clientProperties = configClientProperties();
ConfigServicePropertySourceLocator configServicePropertySourceLocator = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ConfigServicePropertySourceLocator(clientProperties);
configServicePropertySourceLocator.setRestTemplate(customRestTemplate(clientProperties));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> configServicePropertySourceLocator;
}
}</pre><p>
</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">In <code class="literal">resources/META-INF</code>, create a file called
<code class="literal">spring.factories</code> and specify your custom configuration, as shown in the following example:</li></ol></div><p><b>spring.factories.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">org.springframework.cloud.bootstrap.BootstrapConfiguration </span>= com.my.config.client.CustomConfigServiceBootstrapConfiguration</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_vault" href="#_vault"></a>10.8.3&nbsp;Vault</h3></div></div></div><p>When using Vault as a backend to your config server, the client needs to supply a token for the server to retrieve values from Vault.
This token can be provided within the client by setting <code class="literal">spring.cloud.config.token</code>
in <code class="literal">bootstrap.yml</code>, as shown in the following example:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> token</span>: YourVaultToken</pre><p>
</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_nested_keys_in_vault" href="#_nested_keys_in_vault"></a>10.9&nbsp;Nested Keys In Vault</h2></div></div></div><p>Vault supports the ability to nest keys in a value stored in Vault, as shown in the following example:</p><p><code class="literal">echo -n '{"appA": {"secret": "appAsecret"}, "bar": "baz"}' | vault write secret/myapp -</code></p><p>This command writes a JSON object to your Vault.
To access these values in Spring, you would use the traditional dot(<code class="literal">.</code>) annotation, as shown in the following example</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Value("${appA.secret}")</span></em>
String name = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"World"</span>;</pre><p>The preceding code would sets the value of the <code class="literal">name</code> variable to <code class="literal">appAsecret</code>.</p></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_netflix" href="#_spring_cloud_netflix"></a>Part&nbsp;III.&nbsp;Spring Cloud Netflix</h1></div></div></div><div class="partintro"><div></div><p><span class="strong"><strong>Greenwich.M3</strong></span></p><p>This project provides Netflix OSS integrations for Spring Boot apps through autoconfiguration
and binding to the Spring Environment and other Spring programming model idioms. With a few
simple annotations you can quickly enable and configure the common patterns inside your
application and build large distributed systems with battle-tested Netflix components. The
patterns provided include Service Discovery (Eureka), Circuit Breaker (Hystrix),
Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon).</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_service_discovery_eureka_clients" href="#_service_discovery_eureka_clients"></a>11.&nbsp;Service Discovery: Eureka Clients</h2></div></div></div><p>Service Discovery is one of the key tenets of a microservice-based architecture.
Trying to hand-configure each client or some form of convention can be difficult to do and can be brittle.
Eureka is the Netflix Service Discovery Server and Client.
The server can be configured and deployed to be highly available, with each server replicating state about the registered services to the others.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-eureka-client-starter" href="#netflix-eureka-client-starter"></a>11.1&nbsp;How to Include Eureka Client</h2></div></div></div><p>To include the Eureka Client in your project, use the starter with a group ID of <code class="literal">org.springframework.cloud</code> and an artifact ID of <code class="literal">spring-cloud-starter-netflix-eureka-client</code>.
See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_with_eureka" href="#_registering_with_eureka"></a>11.2&nbsp;Registering with Eureka</h2></div></div></div><p>When a client registers with Eureka, it provides meta-data about itself&#8201;&#8212;&#8201;such as host, port, health indicator URL, home page, and other details.
Eureka receives heartbeat messages from each instance belonging to a service.
If the heartbeat fails over a configurable timetable, the instance is normally removed from the registry.</p><p>The following example shows a minimal Eureka client application:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@RequestMapping("/")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello world"</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).web(true).run(args);
}
}</pre><p>Note that the preceding example shows a normal <a class="link" href="https://projects.spring.io/spring-boot/" target="_top">Spring Boot</a> application.
By having <code class="literal">spring-cloud-starter-netflix-eureka-client</code> on the classpath, your application automatically registers with the Eureka Server. Configuration is required to locate the Eureka server, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">eureka:
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/</pre><p>
</p><p>In the preceding example, "defaultZone" is a magic string fallback value that provides the service URL for any client that does not express a preference (in other words, it is a useful default).</p><p>The default application name (that is, the service ID), virtual host, and non-secure port (taken from the <code class="literal">Environment</code>) are <code class="literal">${spring.application.name}</code>, <code class="literal">${spring.application.name}</code> and <code class="literal">${server.port}</code>, respectively.</p><p>Having <code class="literal">spring-cloud-starter-netflix-eureka-client</code> on the classpath makes the app into both a Eureka &#8220;instance&#8221; (that is, it registers itself) and a &#8220;client&#8221; (it can query the registry to locate other services).
The instance behaviour is driven by <code class="literal">eureka.instance.*</code> configuration keys, but the defaults are fine if you ensure that your application has a value for <code class="literal">spring.application.name</code> (this is the default for the Eureka service ID or VIP).</p><p>See <a class="link" href="http://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBean.java" target="_top">EurekaInstanceConfigBean</a> and <a class="link" href="http://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaClientConfigBean.java" target="_top">EurekaClientConfigBean</a> for more details on the configurable options.</p><p>To disable the Eureka Discovery Client, you can set <code class="literal">eureka.client.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_authenticating_with_the_eureka_server" href="#_authenticating_with_the_eureka_server"></a>11.3&nbsp;Authenticating with the Eureka Server</h2></div></div></div><p>HTTP basic authentication is automatically added to your eureka client if one of the <code class="literal">eureka.client.serviceUrl.defaultZone</code> URLs has credentials embedded in it (curl style, as follows: <code class="literal"><a class="link" href="http://user:password@localhost:8761/eureka" target="_top">http://user:password@localhost:8761/eureka</a></code>).
For more complex needs, you can create a <code class="literal">@Bean</code> of type <code class="literal">DiscoveryClientOptionalArgs</code> and inject <code class="literal">ClientFilter</code> instances into it, all of which is applied to the calls from the client to the server.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Because of a limitation in Eureka, it is not possible to support per-server basic auth credentials, so only the first set that are found is used.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_status_page_and_health_indicator" href="#_status_page_and_health_indicator"></a>11.4&nbsp;Status Page and Health Indicator</h2></div></div></div><p>The status page and health indicators for a Eureka instance default to <code class="literal">/info</code> and <code class="literal">/health</code> respectively, which are the default locations of useful endpoints in a Spring Boot Actuator application.
You need to change these, even for an Actuator application if you use a non-default context path or servlet path (such as <code class="literal">server.servletPath=/custom</code>). The following example shows the default values for the two settings:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">eureka:
instance:
statusPageUrlPath: ${server.servletPath}/info
healthCheckUrlPath: ${server.servletPath}/health</pre><p>
</p><p>These links show up in the metadata that is consumed by clients and are used in some scenarios to decide whether to send requests to your application, so it is helpful if they are accurate.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>In Dalston it was also required to set the status and health check URLs when changing
that management context path. This requirement was removed beginning in Edgware.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_a_secure_application" href="#_registering_a_secure_application"></a>11.5&nbsp;Registering a Secure Application</h2></div></div></div><p>If your app wants to be contacted over HTTPS, you can set two flags in the <code class="literal">EurekaInstanceConfig</code>:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">eureka.instance.[nonSecurePortEnabled]=[false]</code></li><li class="listitem"><code class="literal">eureka.instance.[securePortEnabled]=[true]</code></li></ul></div><p>Doing so makes Eureka publish instance information that shows an explicit preference for secure communication.
The Spring Cloud <code class="literal">DiscoveryClient</code> always returns a URI starting with <code class="literal">https</code> for a service configured this way.
Similarly, when a service is configured this way, the Eureka (native) instance information has a secure health check URL.</p><p>Because of the way Eureka works internally, it still publishes a non-secure URL for the status and home pages unless you also override those explicitly.
You can use placeholders to configure the eureka instance URLs, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">eureka:
instance:
statusPageUrl: https://${eureka.hostname}/info
healthCheckUrl: https://${eureka.hostname}/health
homePageUrl: https://${eureka.hostname}/</pre><p>
</p><p>(Note that <code class="literal">${eureka.hostname}</code> is a native placeholder only available
in later versions of Eureka. You could achieve the same thing with
Spring placeholders as well&#8201;&#8212;&#8201;for example, by using <code class="literal">${eureka.instance.hostName}</code>.)</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If your application runs behind a proxy, and the SSL termination is in the proxy (for example, if you run in Cloud Foundry or other platforms as a service), then you need to ensure that the proxy &#8220;forwarded&#8221; headers are intercepted and handled by the application.
If the Tomcat container embedded in a Spring Boot application has explicit configuration for the 'X-Forwarded-\*` headers, this happens automatically.
The links rendered by your app to itself being wrong (the wrong host, port, or protocol) is a sign that you got this configuration wrong.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_eureka_s_health_checks" href="#_eureka_s_health_checks"></a>11.6&nbsp;Eureka&#8217;s Health Checks</h2></div></div></div><p>By default, Eureka uses the client heartbeat to determine if a client is up.
Unless specified otherwise, the Discovery Client does not propagate the current health check status of the application, per the Spring Boot Actuator.
Consequently, after successful registration, Eureka always announces that the application is in 'UP' state. This behavior can be altered by enabling Eureka health checks, which results in propagating application status to Eureka.
As a consequence, every other application does not send traffic to applications in states other then 'UP'.
The following example shows how to enable health checks for the client:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">eureka:
client:
healthcheck:
enabled: true</pre><p>
</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p><code class="literal">eureka.client.healthcheck.enabled=true</code> should only be set in <code class="literal">application.yml</code>. Setting the value in <code class="literal">bootstrap.yml</code> causes undesirable side effects, such as registering in Eureka with an <code class="literal">UNKNOWN</code> status.</p></td></tr></table></div><p>If you require more control over the health checks, consider implementing your own <code class="literal">com.netflix.appinfo.HealthCheckHandler</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_eureka_metadata_for_instances_and_clients" href="#_eureka_metadata_for_instances_and_clients"></a>11.7&nbsp;Eureka Metadata for Instances and Clients</h2></div></div></div><p>It is worth spending a bit of time understanding how the Eureka metadata works, so you can use it in a way that makes sense in your platform.
There is standard metadata for information such as hostname, IP address, port numbers, the status page, and health check.
These are published in the service registry and used by clients to contact the services in a straightforward way.
Additional metadata can be added to the instance registration in the <code class="literal">eureka.instance.metadataMap</code>, and this metadata is accessible in the remote clients.
In general, additional metadata does not change the behavior of the client, unless the client is made aware of the meaning of the metadata.
There are a couple of special cases, described later in this document, where Spring Cloud already assigns meaning to the metadata map.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_eureka_on_cloud_foundry" href="#_using_eureka_on_cloud_foundry"></a>11.7.1&nbsp;Using Eureka on Cloud Foundry</h3></div></div></div><p>Cloud Foundry has a global router so that all instances of the same app have the same hostname (other PaaS solutions with a similar architecture have the same arrangement).
This is not necessarily a barrier to using Eureka.
However, if you use the router (recommended or even mandatory, depending on the way your platform was set up), you need to explicitly set the hostname and port numbers (secure or non-secure) so that they use the router.
You might also want to use instance metadata so that you can distinguish between the instances on the client (for example, in a custom load balancer).
By default, the <code class="literal">eureka.instance.instanceId</code> is <code class="literal">vcap.application.instance_id</code>, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">eureka:
instance:
hostname: ${vcap.application.uris[0]}
nonSecurePort: 80</pre><p>
</p><p>Depending on the way the security rules are set up in your Cloud Foundry instance, you might be able to register and use the IP address of the host VM for direct service-to-service calls.
This feature is not yet available on Pivotal Web Services (<a class="link" href="https://run.pivotal.io" target="_top">PWS</a>).</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_eureka_on_aws" href="#_using_eureka_on_aws"></a>11.7.2&nbsp;Using Eureka on AWS</h3></div></div></div><p>If the application is planned to be deployed to an AWS cloud, the Eureka instance must be configured to be AWS-aware. You can do so by customizing the <a class="link" href="http://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-eureka-client/src/main/java/org/springframework/cloud/netflix/eureka/EurekaInstanceConfigBean.java" target="_top">EurekaInstanceConfigBean</a> as follows:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@Profile("!default")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) {
EurekaInstanceConfigBean b = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> EurekaInstanceConfigBean(inetUtils);
AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"eureka"</span>);
b.setDataCenterInfo(info);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> b;
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_changing_the_eureka_instance_id" href="#_changing_the_eureka_instance_id"></a>11.7.3&nbsp;Changing the Eureka Instance ID</h3></div></div></div><p>A vanilla Netflix Eureka instance is registered with an ID that is equal to its host name (that is, there is only one service per host).
Spring Cloud Eureka provides a sensible default, which is defined as follows:</p><p><code class="literal">${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}</code></p><p>An example is <code class="literal">myhost:myappname:8080</code>.</p><p>By using Spring Cloud, you can override this value by providing a unique identifier in <code class="literal">eureka.instance.instanceId</code>, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">eureka:
instance:
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}</pre><p>
</p><p>With the metadata shown in the preceding example and multiple service instances deployed on localhost, the random value is inserted there to make the instance unique.
In Cloud Foundry, the <code class="literal">vcap.application.instance_id</code> is populated automatically in a Spring Boot application, so the random value is not needed.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_the_eurekaclient" href="#_using_the_eurekaclient"></a>11.8&nbsp;Using the EurekaClient</h2></div></div></div><p>Once you have an application that is a discovery client, you can use it to discover service instances from the <a class="link" href="#spring-cloud-eureka-server" title="12.&nbsp;Service Discovery: Eureka Server">Eureka Server</a>.
One way to do so is to use the native <code class="literal">com.netflix.discovery.EurekaClient</code> (as opposed to the Spring Cloud <code class="literal">DiscoveryClient</code>), as shown in the following example:</p><pre class="screen">@Autowired
private EurekaClient discoveryClient;
public String serviceUrl() {
InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
return instance.getHomePageUrl();
}</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Do not use the <code class="literal">EurekaClient</code> in a <code class="literal">@PostConstruct</code> method or in a <code class="literal">@Scheduled</code> method (or anywhere where the <code class="literal">ApplicationContext</code> might not be started yet).
It is initialized in a <code class="literal">SmartLifecycle</code> (with <code class="literal">phase=0</code>), so the earliest you can rely on it being available is in another <code class="literal">SmartLifecycle</code> with a higher phase.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_eurekaclient_without_jersey" href="#_eurekaclient_without_jersey"></a>11.8.1&nbsp;EurekaClient without Jersey</h3></div></div></div><p>By default, EurekaClient uses Jersey for HTTP communication.
If you wish to avoid dependencies from Jersey, you can exclude it from your dependencies.
Spring Cloud auto-configures a transport client based on Spring <code class="literal">RestTemplate</code>.
The following example shows Jersey being excluded:</p><pre class="screen">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;/artifactId&gt;
&lt;exclusions&gt;
&lt;exclusion&gt;
&lt;groupId&gt;com.sun.jersey&lt;/groupId&gt;
&lt;artifactId&gt;jersey-client&lt;/artifactId&gt;
&lt;/exclusion&gt;
&lt;exclusion&gt;
&lt;groupId&gt;com.sun.jersey&lt;/groupId&gt;
&lt;artifactId&gt;jersey-core&lt;/artifactId&gt;
&lt;/exclusion&gt;
&lt;exclusion&gt;
&lt;groupId&gt;com.sun.jersey.contribs&lt;/groupId&gt;
&lt;artifactId&gt;jersey-apache-client4&lt;/artifactId&gt;
&lt;/exclusion&gt;
&lt;/exclusions&gt;
&lt;/dependency&gt;</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_alternatives_to_the_native_netflix_eurekaclient" href="#_alternatives_to_the_native_netflix_eurekaclient"></a>11.9&nbsp;Alternatives to the Native Netflix EurekaClient</h2></div></div></div><p>You need not use the raw Netflix <code class="literal">EurekaClient</code>.
Also, it is usually more convenient to use it behind a wrapper of some sort.
Spring Cloud has support for <a class="link" href="#spring-cloud-feign" title="22.&nbsp;Declarative REST Client: Feign">Feign</a> (a REST client builder) and <a class="link" href="#spring-cloud-ribbon" title="16.&nbsp;Client Side Load Balancer: Ribbon">Spring <code class="literal">RestTemplate</code></a> through the logical Eureka service identifiers (VIPs) instead of physical URLs.
To configure Ribbon with a fixed list of physical servers, you can set <code class="literal">&lt;client&gt;.ribbon.listOfServers</code> to a comma-separated list of physical addresses (or hostnames), where <code class="literal">&lt;client&gt;</code> is the ID of the client.</p><p>You can also use the <code class="literal">org.springframework.cloud.client.discovery.DiscoveryClient</code>, which provides a simple API (not specific to Netflix) for discovery clients, as shown in the following example:</p><pre class="screen">@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
List&lt;ServiceInstance&gt; list = discoveryClient.getInstances("STORES");
if (list != null &amp;&amp; list.size() &gt; 0 ) {
return list.get(0).getUri();
}
return null;
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_why_is_it_so_slow_to_register_a_service" href="#_why_is_it_so_slow_to_register_a_service"></a>11.10&nbsp;Why Is It so Slow to Register a Service?</h2></div></div></div><p>Being an instance also involves a periodic heartbeat to the registry
(through the client&#8217;s <code class="literal">serviceUrl</code>) with a default duration of 30 seconds.
A service is not available for discovery by clients until the instance, the server, and the client all have the same metadata in their local
cache (so it could take 3 heartbeats).
You can change the period by setting <code class="literal">eureka.instance.leaseRenewalIntervalInSeconds</code>.
Setting it to a value of less than 30 speeds up the process of getting clients connected to other services.
In production, it is probably better to stick with the default, because of internal computations in the server that make assumptions about the lease renewal period.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_zones" href="#_zones"></a>11.11&nbsp;Zones</h2></div></div></div><p>If you have deployed Eureka clients to multiple zones, you may prefer that those clients use services within the same zone before trying services in another zone.
To set that up, you need to configure your Eureka clients correctly.</p><p>First, you need to make sure you have Eureka servers deployed to each zone and that
they are peers of each other.
See the section on <a class="link" href="#spring-cloud-eureka-server-zones-and-regions" title="12.3&nbsp;High Availability, Zones and Regions">zones and regions</a>
for more information.</p><p>Next, you need to tell Eureka which zone your service is in.
You can do so by using the <code class="literal">metadataMap</code> property.
For example, if <code class="literal">service 1</code> is deployed to both <code class="literal">zone 1</code> and <code class="literal">zone 2</code>, you need to set the following Eureka properties in <code class="literal">service 1</code>:</p><p><span class="strong"><strong>Service 1 in Zone 1</strong></span></p><pre class="screen">eureka.instance.metadataMap.zone = zone1
eureka.client.preferSameZoneEureka = true</pre><p><span class="strong"><strong>Service 1 in Zone 2</strong></span></p><pre class="screen">eureka.instance.metadataMap.zone = zone2
eureka.client.preferSameZoneEureka = true</pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-eureka-server" href="#spring-cloud-eureka-server"></a>12.&nbsp;Service Discovery: Eureka Server</h2></div></div></div><p>This section describes how to set up a Eureka server.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-eureka-server-starter" href="#netflix-eureka-server-starter"></a>12.1&nbsp;How to Include Eureka Server</h2></div></div></div><p>To include Eureka Server in your project, use the starter with a group ID of <code class="literal">org.springframework.cloud</code> and an artifact ID of <code class="literal">spring-cloud-starter-netflix-eureka-server</code>.
See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-running-eureka-server" href="#spring-cloud-running-eureka-server"></a>12.2&nbsp;How to Run a Eureka Server</h2></div></div></div><p>The following example shows a minimal Eureka server:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableEurekaServer</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).web(true).run(args);
}
}</pre><p>The server has a home page with a UI and HTTP API endpoints for the normal Eureka functionality under <code class="literal">/eureka/*</code>.</p><p>The following links have some Eureka background reading: <a class="link" href="https://github.com/cfregly/fluxcapacitor/wiki/NetflixOSS-FAQ#eureka-service-discovery-load-balancer" target="_top">flux capacitor</a> and <a class="link" href="https://groups.google.com/forum/?fromgroups#!topic/eureka_netflix/g3p2r7gHnN0" target="_top">google group discussion</a>.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Due to Gradle&#8217;s dependency resolution rules and the lack of a parent bom feature, depending on <code class="literal">spring-cloud-starter-netflix-eureka-server</code> can cause failures on application startup.
To remedy this issue, add the Spring Boot Gradle plugin and import the Spring cloud starter parent bom as follows:</p><p><b>build.gradle.&nbsp;</b>
</p><pre class="programlisting">buildscript {
dependencies {
classpath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}"</span>)
}
}
apply plugin: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spring-boot"</span>
dependencyManagement {
imports {
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}"</span>
}
}</pre><p>
</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-eureka-server-zones-and-regions" href="#spring-cloud-eureka-server-zones-and-regions"></a>12.3&nbsp;High Availability, Zones and Regions</h2></div></div></div><p>The Eureka server does not have a back end store, but the service instances in the registry all have to send heartbeats to keep their registrations up to date (so this can be done in memory).
Clients also have an in-memory cache of Eureka registrations (so they do not have to go to the registry for every request to a service).</p><p>By default, every Eureka server is also a Eureka client and requires (at least one) service URL to locate a peer.
If you do not provide it, the service runs and works, but it fills your logs with a lot of noise about not being able to register with the peer.</p><p>See also <a class="link" href="#spring-cloud-ribbon" title="16.&nbsp;Client Side Load Balancer: Ribbon">below for details of Ribbon support</a> on the client side for Zones and Regions.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-eureka-server-standalone-mode" href="#spring-cloud-eureka-server-standalone-mode"></a>12.4&nbsp;Standalone Mode</h2></div></div></div><p>The combination of the two caches (client and server) and the heartbeats make a standalone Eureka server fairly resilient to failure, as long as there is some sort of monitor or elastic runtime (such as Cloud Foundry) keeping it alive.
In standalone mode, you might prefer to switch off the client side behavior so that it does not keep trying and failing to reach its peers.
The following example shows how to switch off the client-side behavior:</p><p><b>application.yml (Standalone Eureka Server).&nbsp;</b>
</p><pre class="screen">server:
port: 8761
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/</pre><p>
</p><p>Notice that the <code class="literal">serviceUrl</code> is pointing to the same host as the local instance.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-eureka-server-peer-awareness" href="#spring-cloud-eureka-server-peer-awareness"></a>12.5&nbsp;Peer Awareness</h2></div></div></div><p>Eureka can be made even more resilient and available by running multiple instances and asking them to register with each other.
In fact, this is the default behavior, so all you need to do to make it work is add a valid <code class="literal">serviceUrl</code> to a peer, as shown in the following example:</p><p><b>application.yml (Two Peer Aware Eureka Servers).&nbsp;</b>
</p><pre class="screen">---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
client:
serviceUrl:
defaultZone: http://peer2/eureka/
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
client:
serviceUrl:
defaultZone: http://peer1/eureka/</pre><p>
</p><p>In the preceding example, we have a YAML file that can be used to run the same server on two hosts (<code class="literal">peer1</code> and <code class="literal">peer2</code>) by running it in different Spring profiles.
You could use this configuration to test the peer awareness on a single host (there is not much value in doing that in production) by manipulating <code class="literal">/etc/hosts</code> to resolve the host names.
In fact, the <code class="literal">eureka.instance.hostname</code> is not needed if you are running on a machine that knows its own hostname (by default, it is looked up by using <code class="literal">java.net.InetAddress</code>).</p><p>You can add multiple peers to a system, and, as long as they are all connected to each other by at least one edge, they synchronize
the registrations amongst themselves.
If the peers are physically separated (inside a data center or between multiple data centers), then the system can, in principle, survive &#8220;split-brain&#8221; type failures.
You can add multiple peers to a system, and as long as they are all
directly connected to each other, they will synchronize
the registrations amongst themselves.</p><p><b>application.yml (Three Peer Aware Eureka Servers).&nbsp;</b>
</p><pre class="screen">eureka:
client:
serviceUrl:
defaultZone: http://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/
---
spring:
profiles: peer1
eureka:
instance:
hostname: peer1
---
spring:
profiles: peer2
eureka:
instance:
hostname: peer2
---
spring:
profiles: peer3
eureka:
instance:
hostname: peer3</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-eureka-server-prefer-ip-address" href="#spring-cloud-eureka-server-prefer-ip-address"></a>12.6&nbsp;When to Prefer IP Address</h2></div></div></div><p>In some cases, it is preferable for Eureka to advertise the IP addresses of services rather than the hostname.
Set <code class="literal">eureka.instance.preferIpAddress</code> to <code class="literal">true</code> and, when the application registers with eureka, it uses its IP address rather than its hostname.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>If the hostname cannot be determined by Java, then the IP address is sent to Eureka.
Only explict way of setting the hostname is by setting <code class="literal">eureka.instance.hostname</code> property.
You can set your hostname at the run-time by using an environment variable&#8201;&#8212;&#8201;for example, <code class="literal">eureka.instance.hostname=${HOST_NAME}</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_securing_the_eureka_server" href="#_securing_the_eureka_server"></a>12.7&nbsp;Securing The Eureka Server</h2></div></div></div><p>You can secure your Eureka server simply by adding Spring Security to your
server&#8217;s classpath via <code class="literal">spring-boot-starter-security</code>. By default when Spring Security is on the classpath it will require that
a valid CSRF token be sent with every request to the app. Eureka clients will not generally possess a valid
cross site request forgery (CSRF) token you will need to disable this requirement for the <code class="literal">/eureka/**</code> endpoints.
For example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableWebSecurity</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WebSecurityConfig <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> WebSecurityConfigurerAdapter {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> configure(HttpSecurity http) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
http.csrf().ignoringAntMatchers(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/eureka/**"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>.configure(http);
}
}</pre><p>For more information on CSRF see the <a class="link" href="https://docs.spring.io/spring-security/site/docs/current/reference/htmlsingle/#csrf" target="_top">Spring Security documentation</a>.</p><p>A demo Eureka Server can be found in the Spring Cloud Samples <a class="link" href="https://github.com/spring-cloud-samples/eureka/tree/Eureka-With-Security" target="_top">repo</a>.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_circuit_breaker_hystrix_clients" href="#_circuit_breaker_hystrix_clients"></a>13.&nbsp;Circuit Breaker: Hystrix Clients</h2></div></div></div><p>Netflix has created a library called <a class="link" href="https://github.com/Netflix/Hystrix" target="_top">Hystrix</a> that implements the <a class="link" href="http://martinfowler.com/bliki/CircuitBreaker.html" target="_top">circuit breaker pattern</a>.
In a microservice architecture, it is common to have multiple layers of service calls, as shown in the following example:</p><div class="figure"><a name="d0e4183" href="#d0e4183"></a><p class="title"><b>Figure&nbsp;13.1.&nbsp;Microservice Graph</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/master/docs/src/main/asciidoc/images/Hystrix.png" alt="Hystrix"></div></div></div><br class="figure-break"><p>A service failure in the lower level of services can cause cascading failure all the way up to the user.
When calls to a particular service exceed <code class="literal">circuitBreaker.requestVolumeThreshold</code> (default: 20 requests) and the failure percentage is greater than <code class="literal">circuitBreaker.errorThresholdPercentage</code> (default: &gt;50%) in a rolling window defined by <code class="literal">metrics.rollingStats.timeInMilliseconds</code> (default: 10 seconds), the circuit opens and the call is not made.
In cases of error and an open circuit, a fallback can be provided by the developer.</p><div class="figure"><a name="d0e4203" href="#d0e4203"></a><p class="title"><b>Figure&nbsp;13.2.&nbsp;Hystrix fallback prevents cascading failures</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/master/docs/src/main/asciidoc/images/HystrixFallback.png" alt="HystrixFallback"></div></div></div><br class="figure-break"><p>Having an open circuit stops cascading failures and allows overwhelmed or failing services time to recover.
The fallback can be another Hystrix protected call, static data, or a sensible empty value.
Fallbacks may be chained so that the first fallback makes some other business call, which in turn falls back to static data.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_to_include_hystrix" href="#_how_to_include_hystrix"></a>13.1&nbsp;How to Include Hystrix</h2></div></div></div><p>To include Hystrix in your project, use the starter with a group ID of <code class="literal">org.springframework.cloud</code>
and a artifact ID of <code class="literal">spring-cloud-starter-netflix-hystrix</code>.
See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p><p>The following example shows a minimal Eureka server with a Hystrix circuit breaker:</p><pre class="screen">@SpringBootApplication
@EnableCircuitBreaker
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
@Component
public class StoreIntegration {
@HystrixCommand(fallbackMethod = "defaultStores")
public Object getStores(Map&lt;String, Object&gt; parameters) {
//do stuff that might fail
}
public Object defaultStores(Map&lt;String, Object&gt; parameters) {
return /* something useful */;
}
}</pre><p>The <code class="literal">@HystrixCommand</code> is provided by a Netflix contrib library called <a class="link" href="https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica" target="_top">&#8220;javanica&#8221;</a>.
Spring Cloud automatically wraps Spring beans with that annotation in a proxy that is connected to the Hystrix circuit breaker.
The circuit breaker calculates when to open and close the circuit and what to do in case of a failure.</p><p>To configure the <code class="literal">@HystrixCommand</code> you can use the <code class="literal">commandProperties</code>
attribute with a list of <code class="literal">@HystrixProperty</code> annotations. See
<a class="link" href="https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica#configuration" target="_top">here</a>
for more details. See the <a class="link" href="https://github.com/Netflix/Hystrix/wiki/Configuration" target="_top">Hystrix wiki</a>
for details on the properties available.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-hystrix-starter" href="#netflix-hystrix-starter"></a>13.2&nbsp;Propagating the Security Context or Using Spring Scopes</h2></div></div></div><p>If you want some thread local context to propagate into a <code class="literal">@HystrixCommand</code>, the default declaration does not work, because it executes the command in a thread pool (in case of timeouts).
You can switch Hystrix to use the same thread as the caller through configuration or directly in the annotation, by asking it to use a different &#8220;Isolation Strategy&#8221;.
The following example demonstrates setting the thread in the annotation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@HystrixCommand(fallbackMethod = "stubMyService",
commandProperties = {
@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
}
)</span></em>
...</pre><p>The same thing applies if you are using <code class="literal">@SessionScope</code> or <code class="literal">@RequestScope</code>.
If you encounter a runtime exception that says it cannot find the scoped context, you need to use the same thread.</p><p>You also have the option to set the <code class="literal">hystrix.shareSecurityContext</code> property to <code class="literal">true</code>.
Doing so auto-configures a Hystrix concurrency strategy plugin hook to transfer the <code class="literal">SecurityContext</code> from your main thread to the one used by the Hystrix command.
Hystrix does not let multiple Hystrix concurrency strategy be registered so an extension mechanism is available by declaring your own <code class="literal">HystrixConcurrencyStrategy</code> as a Spring bean.
Spring Cloud looks for your implementation within the Spring context and wrap it inside its own plugin.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_health_indicator_4" href="#_health_indicator_4"></a>13.3&nbsp;Health Indicator</h2></div></div></div><p>The state of the connected circuit breakers are also exposed in the <code class="literal">/health</code> endpoint of the calling application, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"hystrix"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"openCircuitBreakers"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"StoreIntegration::getStoresByLocationLink"</span>
]<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"CIRCUIT_OPEN"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UP"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_hystrix_metrics_stream" href="#_hystrix_metrics_stream"></a>13.4&nbsp;Hystrix Metrics Stream</h2></div></div></div><p>To enable the Hystrix metrics stream, include a dependency on <code class="literal">spring-boot-starter-actuator</code> and set
<code class="literal">management.endpoints.web.exposure.include: hystrix.stream</code>.
Doing so exposes the <code class="literal">/actuator/hystrix.stream</code> as a management endpoint, as shown in the following example:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-actuator<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_circuit_breaker_hystrix_dashboard" href="#_circuit_breaker_hystrix_dashboard"></a>14.&nbsp;Circuit Breaker: Hystrix Dashboard</h2></div></div></div><p>One of the main benefits of Hystrix is the set of metrics it gathers about each HystrixCommand.
The Hystrix Dashboard displays the health of each circuit breaker in an efficient manner.</p><div class="figure"><a name="d0e4320" href="#d0e4320"></a><p class="title"><b>Figure&nbsp;14.1.&nbsp;Hystrix Dashboard</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/master/docs/src/main/asciidoc/images/Hystrix.png" alt="Hystrix"></div></div></div><br class="figure-break"></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_hystrix_timeouts_and_ribbon_clients" href="#_hystrix_timeouts_and_ribbon_clients"></a>15.&nbsp;Hystrix Timeouts And Ribbon Clients</h2></div></div></div><p>When using Hystrix commands that wrap Ribbon clients you want to make sure your Hystrix timeout
is configured to be longer than the configured Ribbon timeout, including any potential
retries that might be made. For example, if your Ribbon connection timeout is one second and
the Ribbon client might retry the request three times, than your Hystrix timeout should
be slightly more than three seconds.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-hystrix-dashboard-starter" href="#netflix-hystrix-dashboard-starter"></a>15.1&nbsp;How to Include the Hystrix Dashboard</h2></div></div></div><p>To include the Hystrix Dashboard in your project, use the starter with a group ID of <code class="literal">org.springframework.cloud</code> and an artifact ID of <code class="literal">spring-cloud-starter-netflix-hystrix-dashboard</code>.
See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p><p>To run the Hystrix Dashboard, annotate your Spring Boot main class with <code class="literal">@EnableHystrixDashboard</code>.
Then visit <code class="literal">/hystrix</code> and point the dashboard to an individual instance&#8217;s <code class="literal">/hystrix.stream</code> endpoint in a Hystrix client application.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When connecting to a <code class="literal">/hystrix.stream</code> endpoint that uses HTTPS, the certificate used by the server must be trusted by the JVM.
If the certificate is not trusted, you must import the certificate into the JVM in order for the Hystrix Dashboard to make a successful connection to the stream endpoint.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_turbine" href="#_turbine"></a>15.2&nbsp;Turbine</h2></div></div></div><p>Looking at an individual instance&#8217;s Hystrix data is not very useful in terms of the overall health of the system. <a class="link" href="https://github.com/Netflix/Turbine" target="_top">Turbine</a> is an application that aggregates all of the relevant <code class="literal">/hystrix.stream</code> endpoints into a combined <code class="literal">/turbine.stream</code> for use in the Hystrix Dashboard.
Individual instances are located through Eureka.
Running Turbine requires annotating your main class with the <code class="literal">@EnableTurbine</code> annotation (for example, by using spring-cloud-starter-netflix-turbine to set up the classpath).
All of the documented configuration properties from <a class="link" href="https://github.com/Netflix/Turbine/wiki/Configuration-(1.x)" target="_top">the Turbine 1 wiki</a> apply.
The only difference is that the <code class="literal">turbine.instanceUrlSuffix</code> does not need the port prepended, as this is handled automatically unless <code class="literal">turbine.instanceInsertPort=false</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>By default, Turbine looks for the <code class="literal">/hystrix.stream</code> endpoint on a registered instance by looking up its <code class="literal">hostName</code> and <code class="literal">port</code> entries in Eureka and then appending <code class="literal">/hystrix.stream</code> to it.
If the instance&#8217;s metadata contains <code class="literal">management.port</code>, it is used instead of the <code class="literal">port</code> value for the <code class="literal">/hystrix.stream</code> endpoint.
By default, the metadata entry called <code class="literal">management.port</code> is equal to the <code class="literal">management.port</code> configuration property.
It can be overridden though with following configuration:</p></td></tr></table></div><pre class="screen">eureka:
instance:
metadata-map:
management.port: ${management.port:8081}</pre><p>The <code class="literal">turbine.appConfig</code> configuration key is a list of Eureka serviceIds that turbine uses to lookup instances.
The turbine stream is then used in the Hystrix dashboard with a URL similar to the following:</p><p><code class="literal"><a class="link" href="http://my.turbine.server:8080/turbine.stream?cluster=CLUSTERNAME" target="_top">http://my.turbine.server:8080/turbine.stream?cluster=CLUSTERNAME</a></code></p><p>The cluster parameter can be omitted if the name is <code class="literal">default</code>.
The <code class="literal">cluster</code> parameter must match an entry in <code class="literal">turbine.aggregator.clusterConfig</code>.
Values returned from Eureka are upper-case. Consequently, the following example works if there is an application called <code class="literal">customers</code> registered with Eureka:</p><pre class="screen">turbine:
aggregator:
clusterConfig: CUSTOMERS
appConfig: customers</pre><p>If you need to customize which cluster names should be used by Turbine (because you do not want to store cluster names in
<code class="literal">turbine.aggregator.clusterConfig</code> configuration), provide a bean of type <code class="literal">TurbineClustersProvider</code>.</p><p>The <code class="literal">clusterName</code> can be customized by a SPEL expression in <code class="literal">turbine.clusterNameExpression</code> with root as an instance of <code class="literal">InstanceInfo</code>.
The default value is <code class="literal">appName</code>, which means that the Eureka <code class="literal">serviceId</code> becomes the cluster key (that is, the <code class="literal">InstanceInfo</code> for customers has an <code class="literal">appName</code> of <code class="literal">CUSTOMERS</code>).
A different example is <code class="literal">turbine.clusterNameExpression=aSGName</code>, which gets the cluster name from the AWS ASG name.
The following listing shows another example:</p><pre class="screen">turbine:
aggregator:
clusterConfig: SYSTEM,USER
appConfig: customers,stores,ui,admin
clusterNameExpression: metadata['cluster']</pre><p>In the preceding example, the cluster name from four services is pulled from their metadata map and is expected to have values that include <code class="literal">SYSTEM</code> and <code class="literal">USER</code>.</p><p>To use the &#8220;default&#8221; cluster for all apps, you need a string literal expression (with single quotes and escaped with double quotes if it is in YAML as well):</p><pre class="screen">turbine:
appConfig: customers,stores
clusterNameExpression: "'default'"</pre><p>Spring Cloud provides a <code class="literal">spring-cloud-starter-netflix-turbine</code> that has all the dependencies you need to get a Turbine server running. To ad Turnbine, create a Spring Boot application and annotate it with <code class="literal">@EnableTurbine</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>By default, Spring Cloud lets Turbine use the host and port to allow multiple processes per host, per cluster.
If you want the native Netflix behavior built into Turbine to <span class="emphasis"><em>not</em></span> allow multiple processes per host, per cluster (the key to the instance ID is the hostname), set <code class="literal">turbine.combineHostPort=false</code>.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_clusters_endpoint" href="#_clusters_endpoint"></a>15.2.1&nbsp;Clusters Endpoint</h3></div></div></div><p>In some situations it might be useful for other applications to know what custers have been configured
in Turbine. To support this you can use the <code class="literal">/clusters</code> endpoint which will return a JSON array of
all the configured clusters.</p><p><b>GET /clusters.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"RACES"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"link"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:8383/turbine.stream?cluster=RACES"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"WEB"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"link"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:8383/turbine.stream?cluster=WEB"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span></pre><p>
</p><p>This endpoint can be disabled by setting <code class="literal">turbine.endpoints.clusters.enabled</code> to <code class="literal">false</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_turbine_stream" href="#_turbine_stream"></a>15.3&nbsp;Turbine Stream</h2></div></div></div><p>In some environments (such as in a PaaS setting), the classic Turbine model of pulling metrics from all the distributed Hystrix commands does not work.
In that case, you might want to have your Hystrix commands push metrics to Turbine. Spring Cloud enables that with messaging.
To do so on the client, add a dependency to <code class="literal">spring-cloud-netflix-hystrix-stream</code> and the <code class="literal">spring-cloud-starter-stream-*</code> of your choice.
See the <a class="link" href="https://docs.spring.io/spring-cloud-stream/docs/current/reference/htmlsingle/" target="_top">Spring Cloud Stream documentation</a> for details on the brokers and how to configure the client credentials. It should work out of the box for a local broker.</p><p>On the server side, create a Spring Boot application and annotate it with <code class="literal">@EnableTurbineStream</code>.
The Turbine Stream server requires the use of Spring Webflux, therefore <code class="literal">spring-boot-starter-webflux</code> needs to be included in your project.
By default <code class="literal">spring-boot-starter-webflux</code> is included when adding <code class="literal">spring-cloud-starter-netflix-turbine-stream</code> to your application.</p><p>You can then point the Hystrix Dashboard to the Turbine Stream Server instead of individual Hystrix streams.
If Turbine Stream is running on port 8989 on myhost, then put <code class="literal"><a class="link" href="http://myhost:8989" target="_top">http://myhost:8989</a></code> in the stream input field in the Hystrix Dashboard.
Circuits are prefixed by their respective <code class="literal">serviceId</code>, followed by a dot (<code class="literal">.</code>), and then the circuit name.</p><p>Spring Cloud provides a <code class="literal">spring-cloud-starter-netflix-turbine-stream</code> that has all the dependencies you need to get a Turbine Stream server running.
You can then add the Stream binder of your choice&#8201;&#8212;&#8201;such as <code class="literal">spring-cloud-starter-stream-rabbit</code>.</p><p>Turbine Stream server also supports the <code class="literal">cluster</code> parameter.
Unlike Turbine server, Turbine Stream uses eureka serviceIds as cluster names and these are not configurable.</p><p>If Turbine Stream server is running on port 8989 on <code class="literal">my.turbine.server</code> and you have two eureka serviceIds <code class="literal">customers</code> and <code class="literal">products</code> in your environment, the following URLs will be available on your Turbine Stream server. <code class="literal">default</code> and empty cluster name will provide all metrics that Turbine Stream server receives.</p><pre class="screen">http://my.turbine.sever:8989/turbine.stream?cluster=customers
http://my.turbine.sever:8989/turbine.stream?cluster=products
http://my.turbine.sever:8989/turbine.stream?cluster=default
http://my.turbine.sever:8989/turbine.stream</pre><p>So, you can use eureka serviceIds as cluster names for your Turbine dashboard (or any compatible dashboard).
You don&#8217;t need to configure any properties like <code class="literal">turbine.appConfig</code>, <code class="literal">turbine.clusterNameExpression</code> and <code class="literal">turbine.aggregator.clusterConfig</code> for your Turbine Stream server.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Turbine Stream server gathers all metrics from the configured input channel with Spring Cloud Stream. It means that it doesn&#8217;t gather Hystrix metrics actively from each instance. It just can provide metrics that were already gathered into the input channel by each instance.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-ribbon" href="#spring-cloud-ribbon"></a>16.&nbsp;Client Side Load Balancer: Ribbon</h2></div></div></div><p>Ribbon is a client-side load balancer that gives you a lot of control over the behavior of HTTP and TCP clients.
Feign already uses Ribbon, so, if you use <code class="literal">@FeignClient</code>, this section also applies.</p><p>A central concept in Ribbon is that of the named client.
Each load balancer is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer (for example, by using the <code class="literal">@FeignClient</code> annotation).
On demand, Spring Cloud creates a new ensemble as an <code class="literal">ApplicationContext</code> for each named client by using
<code class="literal">RibbonClientConfiguration</code>.
This contains (amongst other things) an <code class="literal">ILoadBalancer</code>, a <code class="literal">RestClient</code>, and a <code class="literal">ServerListFilter</code>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-ribbon-starter" href="#netflix-ribbon-starter"></a>16.1&nbsp;How to Include Ribbon</h2></div></div></div><p>To include Ribbon in your project, use the starter with a group ID of <code class="literal">org.springframework.cloud</code> and an artifact ID of <code class="literal">spring-cloud-starter-netflix-ribbon</code>.
See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customizing_the_ribbon_client" href="#_customizing_the_ribbon_client"></a>16.2&nbsp;Customizing the Ribbon Client</h2></div></div></div><p>You can configure some bits of a Ribbon client by using external properties in <code class="literal">&lt;client&gt;.ribbon.*</code>, which is similar to using the Netflix APIs natively, except that you can use Spring Boot configuration files.
The native options can be inspected as static fields in <a class="link" href="https://github.com/Netflix/ribbon/blob/master/ribbon-core/src/main/java/com/netflix/client/config/CommonClientConfigKey.java" target="_top"><code class="literal">CommonClientConfigKey</code></a> (part of ribbon-core).</p><p>Spring Cloud also lets you take full control of the client by declaring additional configuration (on top of the <code class="literal">RibbonClientConfiguration</code>) using <code class="literal">@RibbonClient</code>, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@RibbonClient(name = "custom", configuration = CustomConfiguration.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> TestConfiguration {
}</pre><p>In this case, the client is composed from the components already in <code class="literal">RibbonClientConfiguration</code>, together with any in <code class="literal">CustomConfiguration</code> (where the latter generally overrides the former).</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">CustomConfiguration</code> clas must be a <code class="literal">@Configuration</code> class, but take care that it is not in a <code class="literal">@ComponentScan</code> for the main application context.
Otherwise, it is shared by all the <code class="literal">@RibbonClients</code>. If you use <code class="literal">@ComponentScan</code> (or <code class="literal">@SpringBootApplication</code>), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the <code class="literal">@ComponentScan</code>).</p></td></tr></table></div><p>The following table shows the beans that Spring Cloud Netflix provides by default for Ribbon:</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; " width="60%"><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="center" valign="top">Bean Type</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="center" valign="top">Bean Name</th><th style="border-bottom: 0.5pt solid ; " align="center" valign="top">Class Name</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">IClientConfig</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ribbonClientConfig</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">DefaultClientConfigImpl</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">IRule</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ribbonRule</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ZoneAvoidanceRule</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">IPing</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ribbonPing</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">DummyPing</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ServerList&lt;Server&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ribbonServerList</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ConfigurationBasedServerList</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ServerListFilter&lt;Server&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ribbonServerListFilter</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ZonePreferenceServerListFilter</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ILoadBalancer</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ribbonLoadBalancer</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ZoneAwareLoadBalancer</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ServerListUpdater</code></p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">ribbonServerListUpdater</code></p></td><td style="" align="left" valign="top"><p><code class="literal">PollingServerListUpdater</code></p></td></tr></tbody></table></div><p>Creating a bean of one of those type and placing it in a <code class="literal">@RibbonClient</code> configuration (such as <code class="literal">FooConfiguration</code> above) lets you override each one of the beans described, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FooConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ZonePreferenceServerListFilter serverListFilter() {
ZonePreferenceServerListFilter filter = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ZonePreferenceServerListFilter();
filter.setZone(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myTestZone"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> filter;
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> IPing ribbonPing() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> PingUrl();
}
}</pre><p>The include statement in the preceding example replaces <code class="literal">NoOpPing</code> with <code class="literal">PingUrl</code> and provides a custom <code class="literal">serverListFilter</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customizing_the_default_for_all_ribbon_clients" href="#_customizing_the_default_for_all_ribbon_clients"></a>16.3&nbsp;Customizing the Default for All Ribbon Clients</h2></div></div></div><p>A default configuration can be provided for all Ribbon Clients by using the <code class="literal">@RibbonClients</code> annotation and registering a default configuration, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> RibbonClientDefaultConfigurationTestsConfig {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BazServiceList <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> ConfigurationBasedServerList {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> BazServiceList(IClientConfig config) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>.initWithNiwsConfig(config);
}
}
}
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> DefaultRibbonConfig {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> IRule ribbonRule() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BestAvailableRule();
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> IPing ribbonPing() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> PingUrl();
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ServerList&lt;Server&gt; ribbonServerList(IClientConfig config) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> RibbonClientDefaultConfigurationTestsConfig.BazServiceList(config);
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ServerListSubsetFilter serverListFilter() {
ServerListSubsetFilter filter = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ServerListSubsetFilter();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> filter;
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customizing_the_ribbon_client_by_setting_properties" href="#_customizing_the_ribbon_client_by_setting_properties"></a>16.4&nbsp;Customizing the Ribbon Client by Setting Properties</h2></div></div></div><p>Starting with version 1.2.0, Spring Cloud Netflix now supports customizing Ribbon clients by setting properties to be compatible with the <a class="link" href="https://github.com/Netflix/ribbon/wiki/Working-with-load-balancers#components-of-load-balancer" target="_top">Ribbon documentation</a>.</p><p>This lets you change behavior at start up time in different environments.</p><p>The following list shows the supported properties&gt;:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">&lt;clientName&gt;.ribbon.NFLoadBalancerClassName</code>: Should implement <code class="literal">ILoadBalancer</code></li><li class="listitem"><code class="literal">&lt;clientName&gt;.ribbon.NFLoadBalancerRuleClassName</code>: Should implement <code class="literal">IRule</code></li><li class="listitem"><code class="literal">&lt;clientName&gt;.ribbon.NFLoadBalancerPingClassName</code>: Should implement <code class="literal">IPing</code></li><li class="listitem"><code class="literal">&lt;clientName&gt;.ribbon.NIWSServerListClassName</code>: Should implement <code class="literal">ServerList</code></li><li class="listitem"><code class="literal">&lt;clientName&gt;.ribbon.NIWSServerListFilterClassName</code>: Should implement <code class="literal">ServerListFilter</code></li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Classes defined in these properties have precedence over beans defined by using <code class="literal">@RibbonClient(configuration=MyRibbonConfig.class)</code> and the defaults provided by Spring Cloud Netflix.</p></td></tr></table></div><p>To set the <code class="literal">IRule</code> for a service name called <code class="literal">users</code>, you could set the following properties:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">users:
ribbon:
NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule</pre><p>
</p><p>See the <a class="link" href="https://github.com/Netflix/ribbon/wiki/Working-with-load-balancers" target="_top">Ribbon documentation</a> for implementations provided by Ribbon.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_ribbon_with_eureka" href="#_using_ribbon_with_eureka"></a>16.5&nbsp;Using Ribbon with Eureka</h2></div></div></div><p>When Eureka is used in conjunction with Ribbon (that is, both are on the classpath), the <code class="literal">ribbonServerList</code> is overridden with an extension of <code class="literal">DiscoveryEnabledNIWSServerList</code>, which populates the list of servers from Eureka.
It also replaces the <code class="literal">IPing</code> interface with <code class="literal">NIWSDiscoveryPing</code>, which delegates to Eureka to determine if a server is up.
The <code class="literal">ServerList</code> that is installed by default is a <code class="literal">DomainExtractingServerList</code>. Its purpose is to make metadata available to the load balancer without using AWS AMI metadata (which is what Netflix relies on).
By default, the server list is constructed with &#8220;zone&#8221; information, as provided in the instance metadata (so, on the remote clients, set <code class="literal">eureka.instance.metadataMap.zone</code>).
If that is missing and if the <code class="literal">approximateZoneFromHostname</code> flag is set, it can use the domain name from the server hostname as a proxy for the zone.
Once the zone information is available, it can be used in a <code class="literal">ServerListFilter</code>.
By default, it is used to locate a server in the same zone as the client, because the default is a <code class="literal">ZonePreferenceServerListFilter</code>.
By default, the zone of the client is determined in the same way as the remote instances (that is, through <code class="literal">eureka.instance.metadataMap.zone</code>).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The orthodox &#8220;archaius&#8221; way to set the client zone is through a configuration property called "@zone".
If it is available, Spring Cloud uses that in preference to all other settings (note that the key must be quoted in YAML configuration).</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If there is no other source of zone data, then a guess is made, based on the client configuration (as opposed to the instance configuration).
We take <code class="literal">eureka.client.availabilityZones</code>, which is a map from region name to a list of zones, and pull out the first zone for the instance&#8217;s own region (that is, the <code class="literal">eureka.client.region</code>, which defaults to "us-east-1", for compatibility with native Netflix).</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-ribbon-without-eureka" href="#spring-cloud-ribbon-without-eureka"></a>16.6&nbsp;Example: How to Use Ribbon Without Eureka</h2></div></div></div><p>Eureka is a convenient way to abstract the discovery of remote servers so that you do not have to hard code their URLs in clients.
However, if you prefer not to use Eureka, Ribbon and Feign also work.
Suppose you have declared a <code class="literal">@RibbonClient</code> for "stores", and Eureka is not in use (and not even on the classpath).
The Ribbon client defaults to a configured server list.
You can supply the configuration as follows:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">stores:
ribbon:
listOfServers: example.com,google.com</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_example_disable_eureka_use_in_ribbon" href="#_example_disable_eureka_use_in_ribbon"></a>16.7&nbsp;Example: Disable Eureka Use in Ribbon</h2></div></div></div><p>Setting the <code class="literal">ribbon.eureka.enabled</code> property to <code class="literal">false</code> explicitly disables the use of Eureka in Ribbon, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">ribbon:
eureka:
enabled: false</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_the_ribbon_api_directly" href="#_using_the_ribbon_api_directly"></a>16.8&nbsp;Using the Ribbon API Directly</h2></div></div></div><p>You can also use the <code class="literal">LoadBalancerClient</code> directly, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyClass {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> LoadBalancerClient loadBalancer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doStuff() {
ServiceInstance instance = loadBalancer.choose(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stores"</span>);
URI storesUri = URI.create(String.format(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://%s:%s"</span>, instance.getHost(), instance.getPort()));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ... do something with the URI</span>
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="ribbon-child-context-eager-load" href="#ribbon-child-context-eager-load"></a>16.9&nbsp;Caching of Ribbon Configuration</h2></div></div></div><p>Each Ribbon named client has a corresponding child application Context that Spring Cloud maintains.
This application context is lazily loaded on the first request to the named client.
This lazy loading behavior can be changed to instead eagerly load these child application contexts at startup, by specifying the names of the Ribbon clients, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">ribbon:
eager-load:
enabled: true
clients: client1, client2, client3</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="how-to-configure-hystrix-thread-pools" href="#how-to-configure-hystrix-thread-pools"></a>16.10&nbsp;How to Configure Hystrix Thread Pools</h2></div></div></div><p>If you change <code class="literal">zuul.ribbonIsolationStrategy</code> to <code class="literal">THREAD</code>, the thread isolation strategy for Hystrix is used for all routes.
In that case, the <code class="literal">HystrixThreadPoolKey</code> is set to <code class="literal">RibbonCommand</code> as the default.
It means that HystrixCommands for all routes are executed in the same Hystrix thread pool.
This behavior can be changed with the following configuration:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">zuul:
threadPool:
useSeparateThreadPools: true</pre><p>
</p><p>The preceding example results in HystrixCommands being executed in the Hystrix thread pool for each route.</p><p>In this case, the default <code class="literal">HystrixThreadPoolKey</code> is the same as the service ID for each route.
To add a prefix to <code class="literal">HystrixThreadPoolKey</code>, set <code class="literal">zuul.threadPool.threadPoolKeyPrefix</code> to the value that you want to add, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">zuul:
threadPool:
useSeparateThreadPools: true
threadPoolKeyPrefix: zuulgw</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="how-to-provdie-a-key-to-ribbon" href="#how-to-provdie-a-key-to-ribbon"></a>16.11&nbsp;How to Provide a Key to Ribbon&#8217;s <code class="literal">IRule</code></h2></div></div></div><p>If you need to provide your own <code class="literal">IRule</code> implementation to handle a special routing requirement like a &#8220;canary&#8221; test, pass some information to the <code class="literal">choose</code> method of <code class="literal">IRule</code>.</p><p><b>com.netflix.loadbalancer.IRule.java.&nbsp;</b>
</p><pre class="screen">public interface IRule{
public Server choose(Object key);
:</pre><p>
</p><p>You can provide some information that is used by your <code class="literal">IRule</code> implementation to choose a target server, as shown in the following example:</p><pre class="screen">RequestContext.getCurrentContext()
.set(FilterConstants.LOAD_BALANCER_KEY, "canary-test");</pre><p>If you put any object into the <code class="literal">RequestContext</code> with a key of <code class="literal">FilterConstants.LOAD_BALANCER_KEY</code>, it is passed to the <code class="literal">choose</code> method of the <code class="literal">IRule</code> implementation.
The code shown in the preceding example must be executed before <code class="literal">RibbonRoutingFilter</code> is executed.
Zuul&#8217;s pre filter is the best place to do that.
You can access HTTP headers and query parameters through the <code class="literal">RequestContext</code> in pre filter, so it can be used to determine the <code class="literal">LOAD_BALANCER_KEY</code> that is passed to Ribbon.
If you do not put any value with <code class="literal">LOAD_BALANCER_KEY</code> in <code class="literal">RequestContext</code>, null is passed as a parameter of the <code class="literal">choose</code> method.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_external_configuration_archaius" href="#_external_configuration_archaius"></a>17.&nbsp;External Configuration: Archaius</h2></div></div></div><p><a class="link" href="https://github.com/Netflix/archaius" target="_top">Archaius</a> is the Netflix client-side configuration library.
It is the library used by all of the Netflix OSS components for configuration.
Archaius is an extension of the <a class="link" href="http://commons.apache.org/proper/commons-configuration" target="_top">Apache Commons Configuration</a> project.
It allows updates to configuration by either polling a source for changes or by letting a source push changes to the client.
Archaius uses Dynamic&lt;Type&gt;Property classes as handles to properties, as shown in the following example:</p><p><b>Archaius Example.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ArchaiusTest {
DynamicStringProperty myprop = DynamicPropertyFactory
.getInstance()
.getStringProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"my.prop"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doSomething() {
OtherClass.someMethod(myprop.get());
}
}</pre><p>
</p><p>Archaius has its own set of configuration files and loading priorities.
Spring applications should generally not use Archaius directly, but the need to configure the Netflix tools natively remains.
Spring Cloud has a Spring Environment Bridge so that Archaius can read properties from the Spring Environment.
This bridge allows Spring Boot projects to use the normal configuration toolchain while letting them configure the Netflix tools as documented (for the most part).</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_router_and_filter_zuul" href="#_router_and_filter_zuul"></a>18.&nbsp;Router and Filter: Zuul</h2></div></div></div><p>Routing is an integral part of a microservice architecture.
For example, <code class="literal">/</code> may be mapped to your web application, <code class="literal">/api/users</code> is mapped to the user service and <code class="literal">/api/shop</code> is mapped to the shop service.
<a class="link" href="https://github.com/Netflix/zuul" target="_top">Zuul</a> is a JVM-based router and server-side load balancer from Netflix.</p><p><a class="link" href="http://www.slideshare.net/MikeyCohen1/edge-architecture-ieee-international-conference-on-cloud-engineering-32240146/27" target="_top">Netflix uses Zuul</a> for the following:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Authentication</li><li class="listitem">Insights</li><li class="listitem">Stress Testing</li><li class="listitem">Canary Testing</li><li class="listitem">Dynamic Routing</li><li class="listitem">Service Migration</li><li class="listitem">Load Shedding</li><li class="listitem">Security</li><li class="listitem">Static Response handling</li><li class="listitem">Active/Active traffic management</li></ul></div><p>Zuul&#8217;s rule engine lets rules and filters be written in essentially any JVM language, with built-in support for Java and Groovy.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The configuration property <code class="literal">zuul.max.host.connections</code> has been replaced by two new properties, <code class="literal">zuul.host.maxTotalConnections</code> and <code class="literal">zuul.host.maxPerRouteConnections</code>, which default to 200 and 20 respectively.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The default Hystrix isolation pattern (<code class="literal">ExecutionIsolationStrategy</code>) for all routes is <code class="literal">SEMAPHORE</code>.
<code class="literal">zuul.ribbonIsolationStrategy</code> can be changed to <code class="literal">THREAD</code> if that isolation pattern is preferred.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-zuul-starter" href="#netflix-zuul-starter"></a>18.1&nbsp;How to Include Zuul</h2></div></div></div><p>To include Zuul in your project, use the starter with a group ID of <code class="literal">org.springframework.cloud</code> and a artifact ID of <code class="literal">spring-cloud-starter-netflix-zuul</code>.
See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-zuul-reverse-proxy" href="#netflix-zuul-reverse-proxy"></a>18.2&nbsp;Embedded Zuul Reverse Proxy</h2></div></div></div><p>Spring Cloud has created an embedded Zuul proxy to ease the development of a common use case where a UI application wants to make proxy calls to one or more back end services.
This feature is useful for a user interface to proxy to the back end services it requires, avoiding the need to manage CORS and authentication concerns independently for all the back ends.</p><p>To enable it, annotate a Spring Boot main class with <code class="literal">@EnableZuulProxy</code>. Doing so causes local calls to be forwarded to the appropriate service.
By convention, a service with an ID of <code class="literal">users</code> receives requests from the proxy located at <code class="literal">/users</code> (with the prefix stripped).
The proxy uses Ribbon to locate an instance to which to forward through discovery.
All requests are executed in a <a class="link" href="#hystrix-fallbacks-for-routes" title="18.12&nbsp;Providing Hystrix Fallbacks For Routes">hystrix command</a>, so failures appear in Hystrix metrics.
Once the circuit is open, the proxy does not try to contact the service.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>the Zuul starter does not include a discovery client, so, for routes based on service IDs, you need to provide one of those on the classpath as well (Eureka is one choice).</p></td></tr></table></div><p>To skip having a service automatically added, set <code class="literal">zuul.ignored-services</code> to a list of service ID patterns.
If a service matches a pattern that is ignored but is also included in the explicitly configured routes map, it is unignored, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ignoredServices</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'*'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>: /myusers/**</pre><p>
</p><p>In the preceding example, all services are ignored, <span class="strong"><strong>except</strong></span> for <code class="literal">users</code>.</p><p>To augment or change the proxy routes, you can add external configuration, as follows:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>: /myusers/**</pre><p>
</p><p>The preceding example means that HTTP calls to <code class="literal">/myusers</code> get forwarded to the <code class="literal">users</code> service (for example <code class="literal">/myusers/101</code> is forwarded to <code class="literal">/101</code>).</p><p>To get more fine-grained control over a route, you can specify the path and the serviceId independently, as follows:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /myusers/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> serviceId</span>: users_service</pre><p>
</p><p>The preceding example means that HTTP calls to <code class="literal">/myusers</code> get forwarded to the <code class="literal">users_service</code> service.
The route must have a <code class="literal">path</code> that can be specified as an ant-style pattern, so <code class="literal">/myusers/*</code> only matches one level, but <code class="literal">/myusers/**</code> matches hierarchically.</p><p>The location of the back end can be specified as either a <code class="literal">serviceId</code> (for a service from discovery) or a <code class="literal">url</code> (for a physical location), as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /myusers/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: http://example.com/users_service</pre><p>
</p><p>These simple url-routes do not get executed as a <code class="literal">HystrixCommand</code>, nor do they load-balance multiple URLs with Ribbon.
To achieve those goals, you can specify a <code class="literal">serviceId</code> with a static list of servers, as follows:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> echo</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /myusers/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> serviceId</span>: myusers-service
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stripPrefix</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">hystrix</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> command</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> myusers-service</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> execution</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> isolation</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> thread</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> timeoutInMilliseconds</span>: ...
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">myusers-service</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ribbon</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> NIWSServerListClassName</span>: com.netflix.loadbalancer.ConfigurationBasedServerList
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> listOfServers</span>: http://example1.com,http://example2.com
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ConnectTimeout</span>: <span class="hl-number">1000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ReadTimeout</span>: <span class="hl-number">3000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> MaxTotalHttpConnections</span>: <span class="hl-number">500</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> MaxConnectionsPerHost</span>: <span class="hl-number">100</span></pre><p>
</p><p>Another method is specifiying a service-route and configuring a Ribbon client for the <code class="literal">serviceId</code> (doing so requires disabling Eureka support in Ribbon&#8201;&#8212;&#8201;see <a class="link" href="#spring-cloud-ribbon-without-eureka" title="16.6&nbsp;Example: How to Use Ribbon Without Eureka">above for more information</a>), as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /myusers/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> serviceId</span>: users
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">ribbon</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> eureka</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">users</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ribbon</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> listOfServers</span>: example.com,google.com</pre><p>
</p><p>You can provide a convention between <code class="literal">serviceId</code> and routes by using <code class="literal">regexmapper</code>.
It uses regular-expression named groups to extract variables from <code class="literal">serviceId</code> and inject them into a route pattern, as shown in the following example:</p><p><b>ApplicationConfiguration.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> PatternServiceRouteMapper serviceRouteMapper() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> PatternServiceRouteMapper(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(?&lt;name&gt;^.+)-(?&lt;version&gt;v.+$)"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${version}/${name}"</span>);
}</pre><p>
</p><p>The preceding example means that a <code class="literal">serviceId</code> of <code class="literal">myusers-v1</code> is mapped to route <code class="literal">/v1/myusers/**</code>.
Any regular expression is accepted, but all named groups must be present in both <code class="literal">servicePattern</code> and <code class="literal">routePattern</code>.
If <code class="literal">servicePattern</code> does not match a <code class="literal">serviceId</code>, the default behavior is used.
In the preceding example, a <code class="literal">serviceId</code> of <code class="literal">myusers</code> is mapped to the "/myusers/**" route (with no version detected).
This feature is disabled by default and only applies to discovered services.</p><p>To add a prefix to all mappings, set <code class="literal">zuul.prefix</code> to a value, such as <code class="literal">/api</code>.
By default, the proxy prefix is stripped from the request before the request is forwarded by (you can switch this behavior off with <code class="literal">zuul.stripPrefix=false</code>).
You can also switch off the stripping of the service-specific prefix from individual routes, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /myusers/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stripPrefix</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span></pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><code class="literal">zuul.stripPrefix</code> only applies to the prefix set in <code class="literal">zuul.prefix</code>.
It does not have any effect on prefixes defined within a given route&#8217;s <code class="literal">path</code>.</p></td></tr></table></div><p>In the preceding example, requests to <code class="literal">/myusers/101</code> are forwarded to <code class="literal">/myusers/101</code> on the <code class="literal">users</code> service.</p><p>The <code class="literal">zuul.routes</code> entries actually bind to an object of type <code class="literal">ZuulProperties</code>.
If you look at the properties of that object, you can see that it also has a <code class="literal">retryable</code> flag.
Set that flag to <code class="literal">true</code> to have the Ribbon client automatically retry failed requests.
You can also set that flag to <code class="literal">true</code> when you need to modify the parameters of the retry operations that use the Ribbon client configuration.</p><p>By default, the <code class="literal">X-Forwarded-Host</code> header is added to the forwarded requests.
To turn it off, set <code class="literal">zuul.addProxyHeaders = false</code>.
By default, the prefix path is stripped, and the request to the back end picks up a <code class="literal">X-Forwarded-Prefix</code> header (<code class="literal">/myusers</code> in the examples shown earlier).</p><p>If you set a default route (<code class="literal">/</code>), an application with <code class="literal">@EnableZuulProxy</code> could act as a standalone server. For example, <code class="literal">zuul.route.home: /</code> would route all traffic ("/**") to the "home" service.</p><p>If more fine-grained ignoring is needed, you can specify specific patterns to ignore.
These patterns are evaluated at the start of the route location process, which means prefixes should be included in the pattern to warrant a match.
Ignored patterns span all services and supersede any other route specification.
The following example shows how to create ignored patterns:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ignoredPatterns</span>: /**/admin/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>: /myusers/**</pre><p>
</p><p>The preceding example means that all calls (such as <code class="literal">/myusers/101</code>) are forwarded to <code class="literal">/101</code> on the <code class="literal">users</code> service.
However, calls including <code class="literal">/admin/</code> do not resolve.</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>If you need your routes to have their order preserved, you need to use a YAML file, as the ordering is lost when using a properties file.
The following example shows such a YAML file:</p></td></tr></table></div><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /myusers/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> legacy</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /**</pre><p>
</p><p>If you were to use a properties file, the <code class="literal">legacy</code> path might end up in front of the <code class="literal">users</code>
path, rendering the <code class="literal">users</code> path unreachable.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_zuul_http_client" href="#_zuul_http_client"></a>18.3&nbsp;Zuul Http Client</h2></div></div></div><p>The default HTTP client used by Zuul is now backed by the Apache HTTP Client instead of the deprecated Ribbon <code class="literal">RestClient</code>.
To use <code class="literal">RestClient</code> or <code class="literal">okhttp3.OkHttpClient</code>, set <code class="literal">ribbon.restclient.enabled=true</code> or <code class="literal">ribbon.okhttp.enabled=true</code>, respectively.
If you would like to customize the Apache HTTP client or the OK HTTP client, provide a bean of type <code class="literal">ClosableHttpClient</code> or <code class="literal">OkHttpClient</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_cookies_and_sensitive_headers" href="#_cookies_and_sensitive_headers"></a>18.4&nbsp;Cookies and Sensitive Headers</h2></div></div></div><p>You can share headers between services in the same system, but you probably do not want sensitive headers leaking downstream into external servers.
You can specify a list of ignored headers as part of the route configuration.
Cookies play a special role, because they have well defined semantics in browsers, and they are always to be treated as sensitive.
If the consumer of your proxy is a browser, then cookies for downstream services also cause problems for the user, because they all get jumbled up together (all downstream services look like they come from the same place).</p><p>If you are careful with the design of your services, (for example, if only one of the downstream services sets cookies), you might be able to let them flow from the back end all the way up to the caller.
Also, if your proxy sets cookies and all your back-end services are part of the same system, it can be natural to simply share them (and, for instance, use Spring Session to link them up to some shared state).
Other than that, any cookies that get set by downstream services are likely to be not useful to the caller, so it is recommended that you make (at least) <code class="literal">Set-Cookie</code> and <code class="literal">Cookie</code> into sensitive headers for routes that are not part of your domain.
Even for routes that are part of your domain, try to think carefully about what it means before letting cookies flow between them and the proxy.</p><p>The sensitive headers can be configured as a comma-separated list per route, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /myusers/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> sensitiveHeaders</span>: Cookie,Set-Cookie,Authorization
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: https://downstream</pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>This is the default value for <code class="literal">sensitiveHeaders</code>, so you need not set it unless you want it to be different.
This is new in Spring Cloud Netflix 1.1 (in 1.0, the user had no control over headers, and all cookies flowed in both directions).</p></td></tr></table></div><p>The <code class="literal">sensitiveHeaders</code> are a blacklist, and the default is not empty.
Consequently, to make Zuul send all headers (except the <code class="literal">ignored</code> ones), you must explicitly set it to the empty list.
Doing so is necessary if you want to pass cookie or authorization headers to your back end. The following example shows how to use <code class="literal">sensitiveHeaders</code>:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> users</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /myusers/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> sensitiveHeaders</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: https://downstream</pre><p>
</p><p>You can also set sensitive headers, by setting <code class="literal">zuul.sensitiveHeaders</code>.
If <code class="literal">sensitiveHeaders</code> is set on a route, it overrides the global <code class="literal">sensitiveHeaders</code> setting.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_ignored_headers" href="#_ignored_headers"></a>18.5&nbsp;Ignored Headers</h2></div></div></div><p>In addition to the route-sensitive headers, you can set a global value called <code class="literal">zuul.ignoredHeaders</code> for values (both request and response) that should be discarded during interactions with downstream services.
By default, if Spring Security is not on the classpath, these are empty.
Otherwise, they are initialized to a set of well known &#8220;security&#8221; headers (for example, involving caching) as specified by Spring Security.
The assumption in this case is that the downstream services might add these headers, too, but we want the values from the proxy.
To not discard these well known security headers when Spring Security is on the classpath, you can set <code class="literal">zuul.ignoreSecurityHeaders</code> to <code class="literal">false</code>.
Doing so can be useful if you disabled the HTTP Security response headers in Spring Security and want the values provided by downstream services.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_management_endpoints" href="#_management_endpoints"></a>18.6&nbsp;Management Endpoints</h2></div></div></div><p>By default, if you use <code class="literal">@EnableZuulProxy</code> with the Spring Boot Actuator, you enable two additional endpoints:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Routes</li><li class="listitem">Filters</li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_routes_endpoint" href="#_routes_endpoint"></a>18.6.1&nbsp;Routes Endpoint</h3></div></div></div><p>A GET to the routes endpoint at <code class="literal">/routes</code> returns a list of the mapped routes:</p><p><b>GET /routes.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
/stores/**: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:8081"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
</p><p>Additional route details can be requested by adding the <code class="literal">?format=details</code> query string to <code class="literal">/routes</code>.
Doing so produces the following output:</p><p><b>GET /routes/details.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/stores/**"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stores"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fullPath"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/stores/**"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"location"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:8081"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"path"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/**"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"prefix"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/stores"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"retryable"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"customSensitiveHeaders"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"prefixStripped"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
</p><p>A <code class="literal">POST</code> to <code class="literal">/routes</code> forces a refresh of the existing routes (for example, when there have been changes in the service catalog).
You can disable this endpoint by setting <code class="literal">endpoints.routes.enabled</code> to <code class="literal">false</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>the routes should respond automatically to changes in the service catalog, but the <code class="literal">POST</code> to <code class="literal">/routes</code> is a way to force the change
to happen immediately.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_filters_endpoint" href="#_filters_endpoint"></a>18.6.2&nbsp;Filters Endpoint</h3></div></div></div><p>A <code class="literal">GET</code> to the filters endpoint at <code class="literal">/filters</code> returns a map of Zuul filters by type.
For each filter type in the map, you get a list of all the filters of that type, along with their details.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_strangulation_patterns_and_local_forwards" href="#_strangulation_patterns_and_local_forwards"></a>18.7&nbsp;Strangulation Patterns and Local Forwards</h2></div></div></div><p>A common pattern when migrating an existing application or API is to &#8220;strangle&#8221; old endpoints, slowly replacing them with different implementations.
The Zuul proxy is a useful tool for this because you can use it to handle all traffic from the clients of the old endpoints but redirect some of the requests to new ones.</p><p>The following example shows the configuration details for a &#8220;strangle&#8221; scenario:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> first</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /first/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: http://first.example.com
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> second</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /second/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: forward:/second
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> third</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /third/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: forward:/<span class="hl-number">3</span>rd
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> legacy</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> path</span>: /**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: http://legacy.example.com</pre><p>
</p><p>In the preceding example, we are strangle the &#8220;legacy&#8221; application, which is mapped to all requests that do not match one of the other patterns.
Paths in <code class="literal">/first/**</code> have been extracted into a new service with an external URL.
Paths in <code class="literal">/second/**</code> are forwarded so that they can be handled locally (for example, with a normal Spring <code class="literal">@RequestMapping</code>).
Paths in <code class="literal">/third/**</code> are also forwarded but with a different prefix (<code class="literal">/third/foo</code> is forwarded to <code class="literal">/3rd/foo</code>).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The ignored patterns aren&#8217;t completely ignored, they just are not handled by the proxy (so they are also effectively forwarded locally).</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_uploading_files_through_zuul" href="#_uploading_files_through_zuul"></a>18.8&nbsp;Uploading Files through Zuul</h2></div></div></div><p>If you use <code class="literal">@EnableZuulProxy</code>, you can use the proxy paths to upload files and it should work, so long as the files are small.
For large files there is an alternative path that bypasses the Spring <code class="literal">DispatcherServlet</code> (to avoid multipart processing) in "/zuul/*".
In other words, if you have <code class="literal">zuul.routes.customers=/customers/**</code>, then you can <code class="literal">POST</code> large files to <code class="literal">/zuul/customers/*</code>.
The servlet path is externalized via <code class="literal">zuul.servletPath</code>.
If the proxy route takes you through a Ribbon load balancer, extremely large files also require elevated timeout settings, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds</span>: <span class="hl-number">60000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">ribbon</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ConnectTimeout</span>: <span class="hl-number">3000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ReadTimeout</span>: <span class="hl-number">60000</span></pre><p>
</p><p>Note that, for streaming to work with large files, you need to use chunked encoding in the request (which some browsers do not do by default), as shown in the following example:</p><pre class="programlisting">$ curl -v -H <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Transfer-Encoding: chunked"</span> \
-F <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"file=@mylarge.iso"</span> localhost:<span class="hl-number">9999</span>/zuul/simple/file</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_query_string_encoding" href="#_query_string_encoding"></a>18.9&nbsp;Query String Encoding</h2></div></div></div><p>When processing the incoming request, query params are decoded so that they can be available for possible modifications in Zuul filters.
They are then re-encoded the back end request is rebuilt in the route filters.
The result can be different than the original input if (for example) it was encoded with Javascript&#8217;s <code class="literal">encodeURIComponent()</code> method.
While this causes no issues in most cases, some web servers can be picky with the encoding of complex query string.</p><p>To force the original encoding of the query string, it is possible to pass a special flag to <code class="literal">ZuulProperties</code> so that the query string is taken as is with the <code class="literal">HttpServletRequest::getQueryString</code> method, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> forceOriginalQueryStringEncoding</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>This special flag works only with <code class="literal">SimpleHostRoutingFilter</code>. Also, you loose the ability to easily override
query parameters with <code class="literal">RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)</code>, because
the query string is now fetched directly on the original <code class="literal">HttpServletRequest</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_plain_embedded_zuul" href="#_plain_embedded_zuul"></a>18.10&nbsp;Plain Embedded Zuul</h2></div></div></div><p>If you use <code class="literal">@EnableZuulServer</code> (instead of <code class="literal">@EnableZuulProxy</code>), you can also run a Zuul server without proxying or selectively switch on parts of the proxying platform.
Any beans that you add to the application of type <code class="literal">ZuulFilter</code> are installed automatically (as they are with <code class="literal">@EnableZuulProxy</code>) but without any of the proxy filters being added automatically.</p><p>In that case, the routes into the Zuul server are still specified by configuring "zuul.routes.*", but there is no service discovery and no proxying. Consequently, the "serviceId" and "url" settings are ignored.
The following example maps all paths in "/api/**" to the Zuul filter chain:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> api</span>: /api/**</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_disable_zuul_filters" href="#_disable_zuul_filters"></a>18.11&nbsp;Disable Zuul Filters</h2></div></div></div><p>Zuul for Spring Cloud comes with a number of <code class="literal">ZuulFilter</code> beans enabled by default in both proxy and server mode.
See <a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters" target="_top">the Zuul filters package</a> for the list of filters that you can enable.
If you want to disable one, set <code class="literal">zuul.&lt;SimpleClassName&gt;.&lt;filterType&gt;.disable=true</code>.
By convention, the package after <code class="literal">filters</code> is the Zuul filter type.
For example to disable <code class="literal">org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter</code>, set <code class="literal">zuul.SendResponseFilter.post.disable=true</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="hystrix-fallbacks-for-routes" href="#hystrix-fallbacks-for-routes"></a>18.12&nbsp;Providing Hystrix Fallbacks For Routes</h2></div></div></div><p>When a circuit for a given route in Zuul is tripped, you can provide a fallback response by creating a bean of type <code class="literal">FallbackProvider</code>.
Within this bean, you need to specify the route ID the fallback is for and provide a <code class="literal">ClientHttpResponse</code> to return as a fallback.
The following example shows a relatively simple <code class="literal">FallbackProvider</code> implementation:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyFallbackProvider <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> FallbackProvider {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String getRoute() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"customers"</span>;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ClientHttpResponse fallbackResponse(String route, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Throwable cause) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (cause <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">instanceof</span> HystrixTimeoutException) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> response(HttpStatus.GATEWAY_TIMEOUT);
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> response(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> ClientHttpResponse response(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> HttpStatus status) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ClientHttpResponse() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> HttpStatus getStatusCode() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> status;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> getRawStatusCode() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> status.value();
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String getStatusText() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> status.getReasonPhrase();
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> close() {
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> InputStream getBody() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ByteArrayInputStream(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fallback"</span>.getBytes());
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> HttpHeaders getHeaders() {
HttpHeaders headers = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> headers;
}
};
}
}</pre><p>The following example shows how the route configuration for the previous example might appear:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">zuul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> customers</span>: /customers/**</pre><p>If you would like to provide a default fallback for all routes, you can create a bean of type <code class="literal">FallbackProvider</code> and have the <code class="literal">getRoute</code> method return <code class="literal">*</code> or <code class="literal">null</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyFallbackProvider <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> FallbackProvider {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String getRoute() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"*"</span>;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ClientHttpResponse fallbackResponse(String route, Throwable throwable) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ClientHttpResponse() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> HttpStatus getStatusCode() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> HttpStatus.OK;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> getRawStatusCode() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">200</span>;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String getStatusText() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> close() {
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> InputStream getBody() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ByteArrayInputStream(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fallback"</span>.getBytes());
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> HttpHeaders getHeaders() {
HttpHeaders headers = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> headers;
}
};
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_zuul_timeouts" href="#_zuul_timeouts"></a>18.13&nbsp;Zuul Timeouts</h2></div></div></div><p>If you want to configure the socket timeouts and read timeouts for requests proxied through Zuul, you have two options, based on your configuration:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">If Zuul uses service discovery, you need to configure these timeouts with the
<code class="literal">ribbon.ReadTimeout</code> and <code class="literal">ribbon.SocketTimeout</code> Ribbon properties.</li></ul></div><p>If you have configured Zuul routes by specifying URLs, you need to use
<code class="literal">zuul.host.connect-timeout-millis</code> and <code class="literal">zuul.host.socket-timeout-millis</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="zuul-redirect-location-rewrite" href="#zuul-redirect-location-rewrite"></a>18.14&nbsp;Rewriting the <code class="literal">Location</code> header</h2></div></div></div><p>If Zuul is fronting a web application, you may need to re-write the <code class="literal">Location</code> header when the web application redirects through a HTTP status code of <code class="literal">3XX</code>.
Otherwise, the browser redirects to the web application&#8217;s URL instead of the Zuul URL.
You can configure a <code class="literal">LocationRewriteFilter</code> Zuul filter to re-write the <code class="literal">Location</code> header to the Zuul&#8217;s URL.
It also adds back the stripped global and route-specific prefixes.
The following example adds a filter by using a Spring Configuration file:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.netflix.zuul.filters.post.LocationRewriteFilter;
...
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableZuulProxy</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ZuulConfig {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> LocationRewriteFilter locationRewriteFilter() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LocationRewriteFilter();
}
}</pre><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p>Use this filter carefully. The filter acts on the <code class="literal">Location</code> header of ALL <code class="literal">3XX</code> response codes, which may not be appropriate in all scenarios, such as when redirecting the user to an external URL.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_enabling_cross_origin_requests" href="#_enabling_cross_origin_requests"></a>18.15&nbsp;Enabling Cross Origin Requests</h2></div></div></div><p>By default Zuul routes all Cross Origin requests (CORS) to the services. If you want instead Zuul to handle these requests it can be done by providing custom <code class="literal">WebMvcConfigurer</code> bean:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> WebMvcConfigurer corsConfigurer() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WebMvcConfigurer() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> addCorsMappings(CorsRegistry registry) {
registry.addMapping(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/path-1/**"</span>)
.allowedOrigins(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://allowed-origin.com"</span>)
.allowedMethods(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"GET"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span>);
}
};
}</pre><p>In the example above, we allow <code class="literal">GET</code> and <code class="literal">POST</code> methods from <code class="literal"><a class="link" href="http://allowed-origin.com" target="_top">http://allowed-origin.com</a></code> to send cross-origin requests to the endpoints starting with <code class="literal">path-1</code>.
You can apply CORS configuration to a specific path pattern or globally for the whole application, using <code class="literal">/**</code> mapping.
You can customize properties: <code class="literal">allowedOrigins</code>,<code class="literal">allowedMethods</code>,<code class="literal">allowedHeaders</code>,<code class="literal">exposedHeaders</code>,<code class="literal">allowCredentials</code> and <code class="literal">maxAge</code> via this configuration.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_metrics" href="#_metrics"></a>18.16&nbsp;Metrics</h2></div></div></div><p>Zuul will provide metrics under the Actuator metrics endpoint for any failures that might occur when routing requests.
These metrics can be viewed by hitting <code class="literal">/actuator/metrics</code>. The metrics will have a name that has the format
<code class="literal">ZUUL::EXCEPTION:errorCause:statusCode</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="zuul-developer-guide" href="#zuul-developer-guide"></a>18.17&nbsp;Zuul Developer Guide</h2></div></div></div><p>For a general overview of how Zuul works, see <a class="link" href="https://github.com/Netflix/zuul/wiki/How-it-Works" target="_top">the Zuul Wiki</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_the_zuul_servlet" href="#_the_zuul_servlet"></a>18.17.1&nbsp;The Zuul Servlet</h3></div></div></div><p>Zuul is implemented as a Servlet. For the general cases, Zuul is embedded into the Spring Dispatch mechanism. This lets Spring MVC be in control of the routing.
In this case, Zuul buffers requests.
If there is a need to go through Zuul without buffering requests (for example, for large file uploads), the Servlet is also installed outside of the Spring Dispatcher.
By default, the servlet has an address of <code class="literal">/zuul</code>.
This path can be changed with the <code class="literal">zuul.servlet-path</code> property.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_zuul_requestcontext" href="#_zuul_requestcontext"></a>18.17.2&nbsp;Zuul RequestContext</h3></div></div></div><p>To pass information between filters, Zuul uses a <a class="link" href="https://github.com/Netflix/zuul/blob/1.x/zuul-core/src/main/java/com/netflix/zuul/context/RequestContext.java" target="_top"><code class="literal">RequestContext</code></a>.
Its data is held in a <code class="literal">ThreadLocal</code> specific to each request.
Information about where to route requests, errors, and the actual <code class="literal">HttpServletRequest</code> and <code class="literal">HttpServletResponse</code> are stored there.
The <code class="literal">RequestContext</code> extends <code class="literal">ConcurrentHashMap</code>, so anything can be stored in the context. <a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/support/FilterConstants.java" target="_top"><code class="literal">FilterConstants</code></a> contains the keys used by the filters installed by Spring Cloud Netflix (more on these <a class="link" href="#zuul-developer-guide-enable-filters" title="18.17.4&nbsp;@EnableZuulServer Filters">later</a>).</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="__literal_enablezuulproxy_literal_vs_literal_enablezuulserver_literal" href="#__literal_enablezuulproxy_literal_vs_literal_enablezuulserver_literal"></a>18.17.3&nbsp;<code class="literal">@EnableZuulProxy</code> vs. <code class="literal">@EnableZuulServer</code></h3></div></div></div><p>Spring Cloud Netflix installs a number of filters, depending on which annotation was used to enable Zuul. <code class="literal">@EnableZuulProxy</code> is a superset of <code class="literal">@EnableZuulServer</code>. In other words, <code class="literal">@EnableZuulProxy</code> contains all the filters installed by <code class="literal">@EnableZuulServer</code>. The additional filters in the &#8220;proxy&#8221; enable routing functionality. If you want a &#8220;blank&#8221; Zuul, you should use <code class="literal">@EnableZuulServer</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="zuul-developer-guide-enable-filters" href="#zuul-developer-guide-enable-filters"></a>18.17.4&nbsp;<code class="literal">@EnableZuulServer</code> Filters</h3></div></div></div><p><code class="literal">@EnableZuulServer</code> creates a <code class="literal">SimpleRouteLocator</code> that loads route definitions from Spring Boot configuration files.</p><p>The following filters are installed (as normal Spring Beans):</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">Pre filters:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">ServletDetectionFilter</code>: Detects whether the request is through the Spring Dispatcher. Sets a boolean with a key of <code class="literal">FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY</code>.</li><li class="listitem"><code class="literal">FormBodyWrapperFilter</code>: Parses form data and re-encodes it for downstream requests.</li><li class="listitem"><code class="literal">DebugFilter</code>: If the <code class="literal">debug</code> request parameter is set, sets <code class="literal">RequestContext.setDebugRouting()</code> and <code class="literal">RequestContext.setDebugRequest()</code> to <code class="literal">true</code>.
*Route filters:</li><li class="listitem"><code class="literal">SendForwardFilter</code>: Forwards requests by using the Servlet <code class="literal">RequestDispatcher</code>. The forwarding location is stored in the <code class="literal">RequestContext</code> attribute, <code class="literal">FilterConstants.FORWARD_TO_KEY</code>. This is useful for forwarding to endpoints in the current application.</li></ul></div></li><li class="listitem"><p class="simpara">Post filters:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">SendResponseFilter</code>: Writes responses from proxied requests to the current response.</li></ul></div></li><li class="listitem"><p class="simpara">Error filters:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">SendErrorFilter</code>: Forwards to <code class="literal">/error</code> (by default) if <code class="literal">RequestContext.getThrowable()</code> is not null. You can change the default forwarding path (<code class="literal">/error</code>) by setting the <code class="literal">error.path</code> property.</li></ul></div></li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="__literal_enablezuulproxy_literal_filters" href="#__literal_enablezuulproxy_literal_filters"></a>18.17.5&nbsp;<code class="literal">@EnableZuulProxy</code> Filters</h3></div></div></div><p>Creates a <code class="literal">DiscoveryClientRouteLocator</code> that loads route definitions from a <code class="literal">DiscoveryClient</code> (such as Eureka) as well as from properties. A route is created for each <code class="literal">serviceId</code> from the <code class="literal">DiscoveryClient</code>. As new services are added, the routes are refreshed.</p><p>In addition to the filters described earlier, the following filters are installed (as normal Spring Beans):</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">Pre filters:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">PreDecorationFilter</code>: Determines where and how to route, depending on the supplied <code class="literal">RouteLocator</code>. It also sets various proxy-related headers for downstream requests.</li></ul></div></li><li class="listitem"><p class="simpara">Route filters:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><p class="simpara"><code class="literal">RibbonRoutingFilter</code>: Uses Ribbon, Hystrix, and pluggable HTTP clients to send requests. Service IDs are found in the <code class="literal">RequestContext</code> attribute, <code class="literal">FilterConstants.SERVICE_ID_KEY</code>. This filter can use different HTTP clients:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: square; "><li class="listitem">Apache <code class="literal">HttpClient</code>: The default client.</li><li class="listitem">Squareup <code class="literal">OkHttpClient</code> v3: Enabled by having the <code class="literal">com.squareup.okhttp3:okhttp</code> library on the classpath and setting <code class="literal">ribbon.okhttp.enabled=true</code>.</li><li class="listitem">Netflix Ribbon HTTP client: Enabled by setting <code class="literal">ribbon.restclient.enabled=true</code>. This client has limitations, including that it does not support the PATCH method, but it also has built-in retry.</li></ul></div></li><li class="listitem"><code class="literal">SimpleHostRoutingFilter</code>: Sends requests to predetermined URLs through an Apache HttpClient. URLs are found in <code class="literal">RequestContext.getRouteHost()</code>.</li></ul></div></li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_zuul_filter_examples" href="#_custom_zuul_filter_examples"></a>18.17.6&nbsp;Custom Zuul Filter Examples</h3></div></div></div><p>Most of the following "How to Write" examples below are included <a class="link" href="https://github.com/spring-cloud-samples/sample-zuul-filters" target="_top">Sample Zuul Filters</a> project. There are also examples of manipulating the request or response body in that repository.</p><p>This section includes the following examples:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#zuul-developer-guide-sample-pre-filter" title="How to Write a Pre Filter">the section called &#8220;How to Write a Pre Filter&#8221;</a></li><li class="listitem"><a class="xref" href="#zuul-developer-guide-sample-route-filter" title="How to Write a Route Filter">the section called &#8220;How to Write a Route Filter&#8221;</a></li><li class="listitem"><a class="xref" href="#zuul-developer-guide-sample-post-filter" title="How to Write a Post Filter">the section called &#8220;How to Write a Post Filter&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="zuul-developer-guide-sample-pre-filter" href="#zuul-developer-guide-sample-pre-filter"></a>How to Write a Pre Filter</h4></div></div></div><p>Pre filters set up data in the <code class="literal">RequestContext</code> for use in filters downstream.
The main use case is to set information required for route filters.
The following example shows a Zuul pre filter:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> QueryParamPreFilter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> ZuulFilter {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> filterOrder() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> PRE_DECORATION_FILTER_ORDER - <span class="hl-number">1</span>; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// run before PreDecoration</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String filterType() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> PRE_TYPE;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> !ctx.containsKey(FORWARD_TO_KEY) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// a filter has already forwarded</span>
&amp;&amp; !ctx.containsKey(SERVICE_ID_KEY); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// a filter has already determined serviceId</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (request.getParameter(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sample"</span>) != null) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// put the serviceId in `RequestContext`</span>
ctx.put(SERVICE_ID_KEY, request.getParameter(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>));
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
}
}</pre><p>The preceding filter populates <code class="literal">SERVICE_ID_KEY</code> from the <code class="literal">sample</code> request parameter.
In practice, you should not do that kind of direct mapping. Instead, the service ID should be looked up from the value of <code class="literal">sample</code> instead.</p><p>Now that <code class="literal">SERVICE_ID_KEY</code> is populated, <code class="literal">PreDecorationFilter</code> does not run and <code class="literal">RibbonRoutingFilter</code> runs.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>If you want to route to a full URL, call <code class="literal">ctx.setRouteHost(url)</code> instead.</p></td></tr></table></div><p>To modify the path to which routing filters forward, set the <code class="literal">REQUEST_URI_KEY</code>.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="zuul-developer-guide-sample-route-filter" href="#zuul-developer-guide-sample-route-filter"></a>How to Write a Route Filter</h4></div></div></div><p>Route filters run after pre filters and make requests to other services.
Much of the work here is to translate request and response data to and from the model required by the client.
The following example shows a Zuul route filter:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> OkHttpRoutingFilter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> ZuulFilter {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> ProxyRequestHelper helper;
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String filterType() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ROUTE_TYPE;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> filterOrder() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> SIMPLE_HOST_ROUTING_FILTER_ORDER - <span class="hl-number">1</span>;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> shouldFilter() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> RequestContext.getCurrentContext().getRouteHost() != null
&amp;&amp; RequestContext.getCurrentContext().sendZuulResponse();
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Object run() {
OkHttpClient httpClient = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> OkHttpClient.Builder()
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// customize</span>
.build();
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
String method = request.getMethod();
String uri = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.helper.buildZuulRequestURI(request);
Headers.Builder headers = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Headers.Builder();
Enumeration&lt;String&gt; headerNames = request.getHeaderNames();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">while</span> (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
Enumeration&lt;String&gt; values = request.getHeaders(name);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">while</span> (values.hasMoreElements()) {
String value = values.nextElement();
headers.add(name, value);
}
}
InputStream inputStream = request.getInputStream();
RequestBody requestBody = null;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (inputStream != null &amp;&amp; HttpMethod.permitsRequestBody(method)) {
MediaType mediaType = null;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>) != null) {
mediaType = MediaType.parse(headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>));
}
requestBody = RequestBody.create(mediaType, StreamUtils.copyToByteArray(inputStream));
}
Request.Builder builder = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Request.Builder()
.headers(headers.build())
.url(uri)
.method(method, requestBody);
Response response = httpClient.newCall(builder.build()).execute();
LinkedMultiValueMap&lt;String, String&gt; responseHeaders = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LinkedMultiValueMap&lt;&gt;();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> (Map.Entry&lt;String, List&lt;String&gt;&gt; entry : response.headers().toMultimap().entrySet()) {
responseHeaders.put(entry.getKey(), entry.getValue());
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.helper.setResponse(response.code(), response.body().byteStream(),
responseHeaders);
context.setRouteHost(null); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// prevent SimpleHostRoutingFilter from running</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
}
}</pre><p>The preceding filter translates Servlet request information into OkHttp3 request information, executes an HTTP request, and translates OkHttp3 response information to the Servlet response.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="zuul-developer-guide-sample-post-filter" href="#zuul-developer-guide-sample-post-filter"></a>How to Write a Post Filter</h4></div></div></div><p>Post filters typically manipulate the response. The following filter adds a random <code class="literal">UUID</code> as the <code class="literal">X-Sample</code> header:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> AddResponseHeaderFilter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> ZuulFilter {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String filterType() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> POST_TYPE;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> filterOrder() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> SEND_RESPONSE_FILTER_ORDER - <span class="hl-number">1</span>;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> shouldFilter() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> true;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletResponse servletResponse = context.getResponse();
servletResponse.addHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"X-Sample"</span>, UUID.randomUUID().toString());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
}
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Other manipulations, such as transforming the response body, are much more complex and computationally intensive.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_zuul_errors_work" href="#_how_zuul_errors_work"></a>18.17.7&nbsp;How Zuul Errors Work</h3></div></div></div><p>If an exception is thrown during any portion of the Zuul filter lifecycle, the error filters are executed.
The <code class="literal">SendErrorFilter</code> is only run if <code class="literal">RequestContext.getThrowable()</code> is not <code class="literal">null</code>.
It then sets specific <code class="literal">javax.servlet.error.*</code> attributes in the request and forwards the request to the Spring Boot error page.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_zuul_eager_application_context_loading" href="#_zuul_eager_application_context_loading"></a>18.17.8&nbsp;Zuul Eager Application Context Loading</h3></div></div></div><p>Zuul internally uses Ribbon for calling the remote URLs.
By default, Ribbon clients are lazily loaded by Spring Cloud on first call.
This behavior can be changed for Zuul by using the following configuration, which results eager loading of the child Ribbon related Application contexts at application startup time.
The following example shows how to enable eager loading:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">zuul:
ribbon:
eager-load:
enabled: true</pre><p>
</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_polyglot_support_with_sidecar" href="#_polyglot_support_with_sidecar"></a>19.&nbsp;Polyglot support with Sidecar</h2></div></div></div><p>Do you have non-JVM languages with which you want to take advantage of Eureka, Ribbon, and Config Server?
The Spring Cloud Netflix Sidecar was inspired by <a class="link" href="https://github.com/Netflix/Prana" target="_top">Netflix Prana</a>.
It includes an HTTP API to get all of the instances (by host and port) for a given service.
You can also proxy service calls through an embedded Zuul proxy that gets its route entries from Eureka.
The Spring Cloud Config Server can be accessed directly through host lookup or through the Zuul Proxy.
The non-JVM application should implement a health check so the Sidecar can report to Eureka whether the app is up or down.</p><p>To include Sidecar in your project, use the dependency with a group ID of <code class="literal">org.springframework.cloud</code>
and artifact ID or <code class="literal">spring-cloud-netflix-sidecar</code>.</p><p>To enable the Sidecar, create a Spring Boot application with <code class="literal">@EnableSidecar</code>.
This annotation includes <code class="literal">@EnableCircuitBreaker</code>, <code class="literal">@EnableDiscoveryClient</code>, and <code class="literal">@EnableZuulProxy</code>.
Run the resulting application on the same host as the non-JVM application.</p><p>To configure the side car, add <code class="literal">sidecar.port</code> and <code class="literal">sidecar.health-uri</code> to <code class="literal">application.yml</code>.
The <code class="literal">sidecar.port</code> property is the port on which the non-JVM application listens.
This is so the Sidecar can properly register the application with Eureka.
The <code class="literal">sidecar.health-uri</code> is a URI accessible on the non-JVM application that mimics a Spring Boot health indicator.
It should return a JSON document that resembles the following:</p><p><b>health-uri-document.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UP"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
</p><p>The following application.yml example shows sample configuration for a Sidecar application:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">5678</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: sidecar
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">sidecar</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">8000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> health-uri</span>: http://localhost:<span class="hl-number">8000</span>/health.json</pre><p>
</p><p>The API for the <code class="literal">DiscoveryClient.getInstances()</code> method is <code class="literal">/hosts/{serviceId}</code>.
The following example response for <code class="literal">/hosts/customers</code> returns two instances on different hosts:</p><p><b>/hosts/customers.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"host"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myhost"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"port"</span>: <span class="hl-number">9000</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"uri"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://myhost:9000"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"serviceId"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"CUSTOMERS"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secure"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"host"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myhost2"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"port"</span>: <span class="hl-number">9000</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"uri"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://myhost2:9000"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"serviceId"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"CUSTOMERS"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secure"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span></pre><p>
</p><p>This API is accessible to the non-JVM application (if the sidecar is on port 5678) at <code class="literal"><a class="link" href="http://localhost:5678/hosts/{serviceId}" target="_top">http://localhost:5678/hosts/{serviceId}</a></code>.</p><p>The Zuul proxy automatically adds routes for each service known in Eureka to <code class="literal">/&lt;serviceId&gt;</code>, so the customers service is available at <code class="literal">/customers</code>.
The non-JVM application can access the customer service at <code class="literal"><a class="link" href="http://localhost:5678/customers" target="_top">http://localhost:5678/customers</a></code> (assuming the sidecar is listening on port 5678).</p><p>If the Config Server is registered with Eureka, the non-JVM application can access it through the Zuul proxy.
If the <code class="literal">serviceId</code> of the ConfigServer is <code class="literal">configserver</code> and the Sidecar is on port 5678, then it can be accessed at <a class="link" href="http://localhost:5678/configserver" target="_top">http://localhost:5678/configserver</a>.</p><p>Non-JVM applications can take advantage of the Config Server&#8217;s ability to return YAML documents.
For example, a call to <a class="link" href="http://sidecar.local.spring.io:5678/configserver/default-master.yml" target="_top">http://sidecar.local.spring.io:5678/configserver/default-master.yml</a>
might result in a YAML document resembling the following:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">eureka</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> client</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> serviceUrl</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> defaultZone</span>: http://localhost:<span class="hl-number">8761</span>/eureka/
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password</span>: password
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">info</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> description</span>: Spring Cloud Samples
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: https://github.com/spring-cloud-samples</pre><p>To enable the health check request to accept all certificates when using HTTPs set <code class="literal">sidecar.accept-all-ssl-certificates</code> to `true.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="retrying-failed-requests" href="#retrying-failed-requests"></a>20.&nbsp;Retrying Failed Requests</h2></div></div></div><p>Spring Cloud Netflix offers a variety of ways to make HTTP requests.
You can use a load balanced <code class="literal">RestTemplate</code>, Ribbon, or Feign.
No matter how you choose to create your HTTP requests, there is always a chance that a request may fail.
When a request fails, you may want to have the request be retried automatically.
To do so when using Sping Cloud Netflix, you need to include <a class="link" href="https://github.com/spring-projects/spring-retry" target="_top">Spring Retry</a> on your application&#8217;s classpath.
When Spring Retry is present, load-balanced <code class="literal">RestTemplates</code>, Feign, and Zuul automatically retry any failed requests (assuming your configuration allows doing so).</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_backoff_policies" href="#_backoff_policies"></a>20.1&nbsp;BackOff Policies</h2></div></div></div><p>By default, no backoff policy is used when retrying requests.
If you would like to configure a backoff policy, you need to create a bean of type <code class="literal">LoadBalancedRetryFactory</code> and override the <code class="literal">createBackOffPolicy</code> method for a given service, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
LoadBalancedRetryFactory retryFactory() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LoadBalancedRetryFactory() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> BackOffPolicy createBackOffPolicy(String service) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ExponentialBackOffPolicy();
}
};
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_configuration" href="#_configuration"></a>20.2&nbsp;Configuration</h2></div></div></div><p>When you use Ribbon with Spring Retry, you can control the retry functionality by configuring certain Ribbon properties.
To do so, set the <code class="literal">client.ribbon.MaxAutoRetries</code>, <code class="literal">client.ribbon.MaxAutoRetriesNextServer</code>, and <code class="literal">client.ribbon.OkToRetryOnAllOperations</code> properties.
See the <a class="link" href="https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties" target="_top">Ribbon documentation</a> for a description of what these properties do.</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Enabling <code class="literal">client.ribbon.OkToRetryOnAllOperations</code> includes retrying POST requests, which can have an impact
on the server&#8217;s resources, due to the buffering of the request body.</p></td></tr></table></div><p>In addition, you may want to retry requests when certain status codes are returned in the response.
You can list the response codes you would like the Ribbon client to retry by setting the <code class="literal">clientName.ribbon.retryableStatusCodes</code> property, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">clientName</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ribbon</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> retryableStatusCodes</span>: <span class="hl-number">404</span>,<span class="hl-number">502</span></pre><p>You can also create a bean of type <code class="literal">LoadBalancedRetryPolicy</code> and implement the <code class="literal">retryableStatusCode</code> method to retry a request given the status code.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_zuul" href="#_zuul"></a>20.2.1&nbsp;Zuul</h3></div></div></div><p>You can turn off Zuul&#8217;s retry functionality by setting <code class="literal">zuul.retryable</code> to <code class="literal">false</code>.
You can also disable retry functionality on a route-by-route basis by setting <code class="literal">zuul.routes.routename.retryable</code> to <code class="literal">false</code>.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_http_clients" href="#_http_clients"></a>21.&nbsp;HTTP Clients</h2></div></div></div><p>Spring Cloud Netflix automatically creates the HTTP client used by Ribbon, Feign, and Zuul for you.
However, you can also provide your own HTTP clients customized as you need them to be.
To do so, you can create a bean of type <code class="literal">ClosableHttpClient</code> if you
are using the Apache Http Cient or <code class="literal">OkHttpClient</code> if you are using OK HTTP.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When you create your own HTTP client, you are also responsible for implementing the correct connection management strategies for these clients.
Doing so improperly can result in resource management issues.</p></td></tr></table></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_openfeign" href="#_spring_cloud_openfeign"></a>Part&nbsp;IV.&nbsp;Spring Cloud OpenFeign</h1></div></div></div><div class="partintro"><div></div><p><span class="strong"><strong>Greenwich.M3</strong></span></p><p>This project provides OpenFeign integrations for Spring Boot apps through autoconfiguration
and binding to the Spring Environment and other Spring programming model idioms.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-feign" href="#spring-cloud-feign"></a>22.&nbsp;Declarative REST Client: Feign</h2></div></div></div><p><a class="link" href="https://github.com/Netflix/feign" target="_top">Feign</a> is a declarative web service client. It makes writing web service clients easier. To use Feign create an interface and annotate it. It has pluggable annotation support including Feign annotations and JAX-RS annotations. Feign also supports pluggable encoders and decoders. Spring Cloud adds support for Spring MVC annotations and for using the same <code class="literal">HttpMessageConverters</code> used by default in Spring Web. Spring Cloud integrates Ribbon and Eureka to provide a load balanced http client when using Feign.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-feign-starter" href="#netflix-feign-starter"></a>22.1&nbsp;How to Include Feign</h2></div></div></div><p>To include Feign in your project use the starter with group <code class="literal">org.springframework.cloud</code>
and artifact id <code class="literal">spring-cloud-starter-openfeign</code>. See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a>
for details on setting up your build system with the current Spring Cloud Release Train.</p><p>Example spring boot app</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableFeignClients</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre><p><b>StoreClient.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@FeignClient("stores")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StoreClient {
<em><span class="hl-annotation" style="color: gray">@RequestMapping(method = RequestMethod.GET, value = "/stores")</span></em>
List&lt;Store&gt; getStores();
<em><span class="hl-annotation" style="color: gray">@RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")</span></em>
Store update(<em><span class="hl-annotation" style="color: gray">@PathVariable("storeId")</span></em> Long storeId, Store store);
}</pre><p>
</p><p>In the <code class="literal">@FeignClient</code> annotation the String value ("stores" above) is
an arbitrary client name, which is used to create a Ribbon load
balancer (see <a class="link" href="#spring-cloud-ribbon" title="16.&nbsp;Client Side Load Balancer: Ribbon">below for details of Ribbon
support</a>). You can also specify a URL using the <code class="literal">url</code> attribute
(absolute value or just a hostname). The name of the bean in the
application context is the fully qualified name of the interface.
To specify your own alias value you can use the <code class="literal">qualifier</code> value
of the <code class="literal">@FeignClient</code> annotation.</p><p>The Ribbon client above will want to discover the physical addresses
for the "stores" service. If your application is a Eureka client then
it will resolve the service in the Eureka service registry. If you
don&#8217;t want to use Eureka, you can simply configure a list of servers
in your external configuration (see
<a class="link" href="#spring-cloud-ribbon-without-eureka" title="16.6&nbsp;Example: How to Use Ribbon Without Eureka">above for example</a>).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-feign-overriding-defaults" href="#spring-cloud-feign-overriding-defaults"></a>22.2&nbsp;Overriding Feign Defaults</h2></div></div></div><p>A central concept in Spring Cloud&#8217;s Feign support is that of the named client. Each feign client is part of an ensemble of components that work together to contact a remote server on demand, and the ensemble has a name that you give it as an application developer using the <code class="literal">@FeignClient</code> annotation. Spring Cloud creates a new ensemble as an
<code class="literal">ApplicationContext</code> on demand for each named client using <code class="literal">FeignClientsConfiguration</code>. This contains (amongst other things) an <code class="literal">feign.Decoder</code>, a <code class="literal">feign.Encoder</code>, and a <code class="literal">feign.Contract</code>.</p><p>Spring Cloud lets you take full control of the feign client by declaring additional configuration (on top of the <code class="literal">FeignClientsConfiguration</code>) using <code class="literal">@FeignClient</code>. Example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@FeignClient(name = "stores", configuration = FooConfiguration.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StoreClient {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//..</span>
}</pre><p>In this case the client is composed from the components already in <code class="literal">FeignClientsConfiguration</code> together with any in <code class="literal">FooConfiguration</code> (where the latter will override the former).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><code class="literal">FooConfiguration</code> does not need to be annotated with <code class="literal">@Configuration</code>. However, if it is, then take care to exclude it from any <code class="literal">@ComponentScan</code> that would otherwise include this configuration as it will become the default source for <code class="literal">feign.Decoder</code>, <code class="literal">feign.Encoder</code>, <code class="literal">feign.Contract</code>, etc., when specified. This can be avoided by putting it in a separate, non-overlapping package from any <code class="literal">@ComponentScan</code> or <code class="literal">@SpringBootApplication</code>, or it can be explicitly excluded in <code class="literal">@ComponentScan</code>.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">serviceId</code> attribute is now deprecated in favor of the <code class="literal">name</code> attribute.</p></td></tr></table></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Previously, using the <code class="literal">url</code> attribute, did not require the <code class="literal">name</code> attribute. Using <code class="literal">name</code> is now required.</p></td></tr></table></div><p>Placeholders are supported in the <code class="literal">name</code> and <code class="literal">url</code> attributes.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@FeignClient(name = "${feign.name}", url = "${feign.url}")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StoreClient {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//..</span>
}</pre><p>Spring Cloud Netflix provides the following beans by default for feign (<code class="literal">BeanType</code> beanName: <code class="literal">ClassName</code>):</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">Decoder</code> feignDecoder: <code class="literal">ResponseEntityDecoder</code> (which wraps a <code class="literal">SpringDecoder</code>)</li><li class="listitem"><code class="literal">Encoder</code> feignEncoder: <code class="literal">SpringEncoder</code></li><li class="listitem"><code class="literal">Logger</code> feignLogger: <code class="literal">Slf4jLogger</code></li><li class="listitem"><code class="literal">Contract</code> feignContract: <code class="literal">SpringMvcContract</code></li><li class="listitem"><code class="literal">Feign.Builder</code> feignBuilder: <code class="literal">HystrixFeign.Builder</code></li><li class="listitem"><code class="literal">Client</code> feignClient: if Ribbon is enabled it is a <code class="literal">LoadBalancerFeignClient</code>, otherwise the default feign client is used.</li></ul></div><p>The OkHttpClient and ApacheHttpClient feign clients can be used by setting <code class="literal">feign.okhttp.enabled</code> or <code class="literal">feign.httpclient.enabled</code> to <code class="literal">true</code>, respectively, and having them on the classpath.
You can customize the HTTP client used by providing a bean of either <code class="literal">ClosableHttpClient</code> when using Apache or <code class="literal">OkHttpClient</code> when using OK HTTP.</p><p>Spring Cloud Netflix <span class="emphasis"><em>does not</em></span> provide the following beans by default for feign, but still looks up beans of these types from the application context to create the feign client:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">Logger.Level</code></li><li class="listitem"><code class="literal">Retryer</code></li><li class="listitem"><code class="literal">ErrorDecoder</code></li><li class="listitem"><code class="literal">Request.Options</code></li><li class="listitem"><code class="literal">Collection&lt;RequestInterceptor&gt;</code></li><li class="listitem"><code class="literal">SetterFactory</code></li></ul></div><p>Creating a bean of one of those type and placing it in a <code class="literal">@FeignClient</code> configuration (such as <code class="literal">FooConfiguration</code> above) allows you to override each one of the beans described. Example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FooConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Contract feignContract() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> feign.Contract.Default();
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BasicAuthRequestInterceptor(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"user"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"password"</span>);
}
}</pre><p>This replaces the <code class="literal">SpringMvcContract</code> with <code class="literal">feign.Contract.Default</code> and adds a <code class="literal">RequestInterceptor</code> to the collection of <code class="literal">RequestInterceptor</code>.</p><p><code class="literal">@FeignClient</code> also can be configured using configuration properties.</p><p>application.yml</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">feign</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> client</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> feignName</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> connectTimeout</span>: <span class="hl-number">5000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> readTimeout</span>: <span class="hl-number">5000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> loggerLevel</span>: full
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> errorDecoder</span>: com.example.SimpleErrorDecoder
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> retryer</span>: com.example.SimpleRetryer
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> requestInterceptors</span>:
- com.example.FooRequestInterceptor
- com.example.BarRequestInterceptor
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> decode404</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> encoder</span>: com.example.SimpleEncoder
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> decoder</span>: com.example.SimpleDecoder
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> contract</span>: com.example.SimpleContract</pre><p>Default configurations can be specified in the <code class="literal">@EnableFeignClients</code> attribute <code class="literal">defaultConfiguration</code> in a similar manner as described above. The difference is that this configuration will apply to <span class="emphasis"><em>all</em></span> feign clients.</p><p>If you prefer using configuration properties to configured all <code class="literal">@FeignClient</code>, you can create configuration properties with <code class="literal">default</code> feign name.</p><p>application.yml</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">feign</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> client</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> default</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> connectTimeout</span>: <span class="hl-number">5000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> readTimeout</span>: <span class="hl-number">5000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> loggerLevel</span>: basic</pre><p>If we create both <code class="literal">@Configuration</code> bean and configuration properties, configuration properties will win.
It will override <code class="literal">@Configuration</code> values. But if you want to change the priority to <code class="literal">@Configuration</code>,
you can change <code class="literal">feign.client.default-to-properties</code> to <code class="literal">false</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you need to use <code class="literal">ThreadLocal</code> bound variables in your <code class="literal">RequestInterceptor`s you will need to either set the
thread isolation strategy for Hystrix to `SEMAPHORE</code> or disable Hystrix in Feign.</p></td></tr></table></div><p>application.yml</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># To disable Hystrix in Feign</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">feign</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> hystrix</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># To set thread isolation to SEMAPHORE</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">hystrix</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> command</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> default</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> execution</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> isolation</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> strategy</span>: SEMAPHORE</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_creating_feign_clients_manually" href="#_creating_feign_clients_manually"></a>22.3&nbsp;Creating Feign Clients Manually</h2></div></div></div><p>In some cases it might be necessary to customize your Feign Clients in a way that is not
possible using the methods above. In this case you can create Clients using the
<a class="link" href="https://github.com/OpenFeign/feign/#basics" target="_top">Feign Builder API</a>. Below is an example
which creates two Feign Clients with the same interface but configures each one with
a separate request interceptor.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Import(FeignClientsConfiguration.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FooController {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> FooClient fooClient;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> FooClient adminClient;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> FooController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.fooClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
.requestInterceptor(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BasicAuthRequestInterceptor(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"user"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"user"</span>))
.target(FooClient.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://PROD-SVC"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.adminClient = Feign.builder().client(client)
.encoder(encoder)
.decoder(decoder)
.contract(contract)
.requestInterceptor(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BasicAuthRequestInterceptor(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"admin"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"admin"</span>))
.target(FooClient.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://PROD-SVC"</span>);
}
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>In the above example <code class="literal">FeignClientsConfiguration.class</code> is the default configuration
provided by Spring Cloud Netflix.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><code class="literal">PROD-SVC</code> is the name of the service the Clients will be making requests to.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The Feign <code class="literal">Contract</code> object defines what annotations and values are valid on interfaces. The
autowired <code class="literal">Contract</code> bean provides supports for SpringMVC annotations, instead of
the default Feign native annotations.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-feign-hystrix" href="#spring-cloud-feign-hystrix"></a>22.4&nbsp;Feign Hystrix Support</h2></div></div></div><p>If Hystrix is on the classpath and <code class="literal">feign.hystrix.enabled=true</code>, Feign will wrap all methods with a circuit breaker. Returning a <code class="literal">com.netflix.hystrix.HystrixCommand</code> is also available. This lets you use reactive patterns (with a call to <code class="literal">.toObservable()</code> or <code class="literal">.observe()</code> or asynchronous use (with a call to <code class="literal">.queue()</code>).</p><p>To disable Hystrix support on a per-client basis create a vanilla <code class="literal">Feign.Builder</code> with the "prototype" scope, e.g.:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FooConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@Scope("prototype")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Feign.Builder feignBuilder() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Feign.builder();
}
}</pre><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Prior to the Spring Cloud Dalston release, if Hystrix was on the classpath Feign would have wrapped
all methods in a circuit breaker by default. This default behavior was changed in Spring Cloud Dalston in
favor for an opt-in approach.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-feign-hystrix-fallback" href="#spring-cloud-feign-hystrix-fallback"></a>22.5&nbsp;Feign Hystrix Fallbacks</h2></div></div></div><p>Hystrix supports the notion of a fallback: a default code path that is executed when they circuit is open or there is an error. To enable fallbacks for a given <code class="literal">@FeignClient</code> set the <code class="literal">fallback</code> attribute to the class name that implements the fallback. You also need to declare your implementation as a Spring bean.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@FeignClient(name = "hello", fallback = HystrixClientFallback.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> HystrixClient {
<em><span class="hl-annotation" style="color: gray">@RequestMapping(method = RequestMethod.GET, value = "/hello")</span></em>
Hello iFailSometimes();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> HystrixClientFallback <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> HystrixClient {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Hello iFailSometimes() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Hello(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fallback"</span>);
}
}</pre><p>If one needs access to the cause that made the fallback trigger, one can use the <code class="literal">fallbackFactory</code> attribute inside <code class="literal">@FeignClient</code>.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> HystrixClient {
<em><span class="hl-annotation" style="color: gray">@RequestMapping(method = RequestMethod.GET, value = "/hello")</span></em>
Hello iFailSometimes();
}
<em><span class="hl-annotation" style="color: gray">@Component</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> HystrixClientFallbackFactory <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> FallbackFactory&lt;HystrixClient&gt; {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> HystrixClient create(Throwable cause) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HystrixClient() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Hello iFailSometimes() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Hello(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fallback; reason was: "</span> + cause.getMessage());
}
};
}
}</pre><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>There is a limitation with the implementation of fallbacks in Feign and how Hystrix fallbacks work. Fallbacks are currently not supported for methods that return <code class="literal">com.netflix.hystrix.HystrixCommand</code> and <code class="literal">rx.Observable</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_feign_and_literal_primary_literal" href="#_feign_and_literal_primary_literal"></a>22.6&nbsp;Feign and <code class="literal">@Primary</code></h2></div></div></div><p>When using Feign with Hystrix fallbacks, there are multiple beans in the <code class="literal">ApplicationContext</code> of the same type. This will cause <code class="literal">@Autowired</code> to not work because there isn&#8217;t exactly one bean, or one marked as primary. To work around this, Spring Cloud Netflix marks all Feign instances as <code class="literal">@Primary</code>, so Spring Framework will know which bean to inject. In some cases, this may not be desirable. To turn off this behavior set the <code class="literal">primary</code> attribute of <code class="literal">@FeignClient</code> to false.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@FeignClient(name = "hello", primary = false)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> HelloClient {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// methods here</span>
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-feign-inheritance" href="#spring-cloud-feign-inheritance"></a>22.7&nbsp;Feign Inheritance Support</h2></div></div></div><p>Feign supports boilerplate apis via single-inheritance interfaces.
This allows grouping common operations into convenient base interfaces.</p><p><b>UserService.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> UserService {
<em><span class="hl-annotation" style="color: gray">@RequestMapping(method = RequestMethod.GET, value ="/users/{id}")</span></em>
User getUser(<em><span class="hl-annotation" style="color: gray">@PathVariable("id")</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> id);
}</pre><p>
</p><p><b>UserResource.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> UserResource <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> UserService {
}</pre><p>
</p><p><b>UserClient.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> project.user;
<em><span class="hl-annotation" style="color: gray">@FeignClient("users")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> UserClient <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> UserService {
}</pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>It is generally not advisable to share an interface between a
server and a client. It introduces tight coupling, and also actually
doesn&#8217;t work with Spring MVC in its current form (method parameter
mapping is not inherited).</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_feign_request_response_compression" href="#_feign_request_response_compression"></a>22.8&nbsp;Feign request/response compression</h2></div></div></div><p>You may consider enabling the request or response GZIP compression for your
Feign requests. You can do this by enabling one of the properties:</p><pre class="programlisting">feign.compression.request.enabled=true
feign.compression.response.enabled=true</pre><p>Feign request compression gives you settings similar to what you may set for your web server:</p><pre class="programlisting">feign.compression.request.enabled=true
feign.compression.request.mime-types=text/xml,application/xml,application/json
feign.compression.request.min-request-size=<span class="hl-number">2048</span></pre><p>These properties allow you to be selective about the compressed media types and minimum request threshold length.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_feign_logging" href="#_feign_logging"></a>22.9&nbsp;Feign logging</h2></div></div></div><p>A logger is created for each Feign client created. By default the name of the logger is the full class name of the interface used to create the Feign client. Feign logging only responds to the <code class="literal">DEBUG</code> level.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">logging.level.project.user.UserClient</span>: DEBUG</pre><p>
</p><p>The <code class="literal">Logger.Level</code> object that you may configure per client, tells Feign how much to log. Choices are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">NONE</code>, No logging (<span class="strong"><strong>DEFAULT</strong></span>).</li><li class="listitem"><code class="literal">BASIC</code>, Log only the request method and URL and the response status code and execution time.</li><li class="listitem"><code class="literal">HEADERS</code>, Log the basic information along with request and response headers.</li><li class="listitem"><code class="literal">FULL</code>, Log the headers, body, and metadata for both requests and responses.</li></ul></div><p>For example, the following would set the <code class="literal">Logger.Level</code> to <code class="literal">FULL</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FooConfiguration {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
Logger.Level feignLoggerLevel() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Logger.Level.FULL;
}
}</pre><pre class="literallayout"> OtherClass.someMethod(myprop.get());
}
}
stripped). The proxy uses Ribbon to locate an instance to forward to
via discovery, and all requests are executed in a
&lt;&lt;hystrix-fallbacks-for-routes, hystrix command&gt;&gt;, so
failures will show up in Hystrix metrics, and once the circuit is open
the proxy will not try to contact the service.</pre></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_stream" href="#_spring_cloud_stream"></a>Part&nbsp;V.&nbsp;Spring Cloud Stream</h1></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_a_brief_history_of_spring_s_data_integration_journey" href="#_a_brief_history_of_spring_s_data_integration_journey"></a>23.&nbsp;A Brief History of Spring&#8217;s Data Integration Journey</h2></div></div></div><p>Spring&#8217;s journey on Data Integration started with <a class="link" href="https://projects.spring.io/spring-integration/" target="_top">Spring Integration</a>. With its programming model, it provided a consistent developer experience to build applications that can embrace <a class="link" href="http://www.enterpriseintegrationpatterns.com/" target="_top">Enterprise Integration Patterns</a> to connect with external systems such as, databases, message brokers, and among others.</p><p>Fast forward to the cloud-era, where microservices have become prominent in the enterprise setting. <a class="link" href="https://projects.spring.io/spring-boot/" target="_top">Spring Boot</a> transformed the way how developers built Applications. With Spring&#8217;s programming model and the runtime responsibilities handled by Spring Boot, it became seamless to develop stand-alone, production-grade Spring-based microservices.</p><p>To extend this to Data Integration workloads, Spring Integration and Spring Boot were put together into a new project. Spring Cloud Stream was born.</p><p>With Spring Cloud Stream, developers can:
* Build, test, iterate, and deploy data-centric applications in isolation.
* Apply modern microservices architecture patterns, including composition through messaging.
* Decouple application responsibilities with event-centric thinking. An event can represent something that has happened in time, to which the downstream consumer applications can react without knowing where it originated or the producer&#8217;s identity.
* Port the business logic onto message brokers (such as RabbitMQ, Apache Kafka, Amazon Kinesis).
* Interoperate between channel-based and non-channel-based application binding scenarios to support stateless and stateful computations by using Project Reactor&#8217;s Flux and Kafka Streams APIs.
* Rely on the framework&#8217;s automatic content-type support for common use-cases. Extending to different data conversion types is possible.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_quick_start_2" href="#_quick_start_2"></a>24.&nbsp;Quick Start</h2></div></div></div><p>You can try Spring Cloud Stream in less then 5 min even before you jump into any details by following this three-step guide.</p><p>We show you how to create a Spring Cloud Stream application that receives messages coming from the messaging middleware of your choice (more on this later) and logs received messages to the console.
We call it <code class="literal">LoggingConsumer</code>.
While not very practical, it provides a good introduction to some of the main concepts
and abstractions, making it easier to digest the rest of this user guide.</p><p>The three steps are as follows:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-creating-sample-application" title="24.1&nbsp;Creating a Sample Application by Using Spring Initializr">Section&nbsp;24.1, &#8220;Creating a Sample Application by Using Spring Initializr&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-importing-project" title="24.2&nbsp;Importing the Project into Your IDE">Section&nbsp;24.2, &#8220;Importing the Project into Your IDE&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-adding-message-handler" title="24.3&nbsp;Adding a Message Handler, Building, and Running">Section&nbsp;24.3, &#8220;Adding a Message Handler, Building, and Running&#8221;</a></li></ol></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-preface-creating-sample-application" href="#spring-cloud-stream-preface-creating-sample-application"></a>24.1&nbsp;Creating a Sample Application by Using Spring Initializr</h2></div></div></div><p>To get started, visit the <a class="link" href="https://start.spring.io" target="_top">Spring Initializr</a>. From there, you can generate our <code class="literal">LoggingConsumer</code> application. To do so:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">In the <span class="strong"><strong>Dependencies</strong></span> section, start typing <code class="literal">stream</code>.
When the &#8220;Cloud Stream&#8221; option should appears, select it.</li><li class="listitem">Start typing either 'kafka' or 'rabbit'.</li><li class="listitem"><p class="simpara">Select &#8220;Kafka&#8221; or &#8220;RabbitMQ&#8221;.</p><p class="simpara">Basically, you choose the messaging middleware to which your application binds.
We recommend using the one you have already installed or feel more comfortable with installing and running.
Also, as you can see from the Initilaizer screen, there are a few other options you can choose.
For example, you can choose Gradle as your build tool instead of Maven (the default).</p></li><li class="listitem"><p class="simpara">In the <span class="strong"><strong>Artifact</strong></span> field, type 'logging-consumer'.</p><p class="simpara">The value of the <span class="strong"><strong>Artifact</strong></span> field becomes the application name.
If you chose RabbitMQ for the middleware, your Spring Initializr should now be as follows:</p><div class="informalfigure"><div class="mediaobject"><img src="images/stream-initializr.png" alt="stream initializr"></div></div></li><li class="listitem"><p class="simpara">Click the <span class="strong"><strong>Generate Project</strong></span> button.</p><p class="simpara">Doing so downloads the zipped version of the generated project to your hard drive.</p></li><li class="listitem">Unzip the file into the folder you want to use as your project directory.</li></ol></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>We encourage you to explore the many possibilities available in the Spring Initializr.
It lets you create many different kinds of Spring applications.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-preface-importing-project" href="#spring-cloud-stream-preface-importing-project"></a>24.2&nbsp;Importing the Project into Your IDE</h2></div></div></div><p>Now you can import the project into your IDE.
Keep in mind that, depending on the IDE, you may need to follow a specific import procedure.
For example, depending on how the project was generated (Maven or Gradle), you may need to follow specific import procedure (for example, in Eclipse or STS, you need to use File &#8594; Import &#8594; Maven &#8594; Existing Maven Project).</p><p>Once imported, the project must have no errors of any kind. Also, <code class="literal">src/main/java</code> should contain <code class="literal">com.example.loggingconsumer.LoggingConsumerApplication</code>.</p><p>Technically, at this point, you can run the application&#8217;s main class.
It is already a valid Spring Boot application.
However, it does not do anything, so we want to add some code.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-preface-adding-message-handler" href="#spring-cloud-stream-preface-adding-message-handler"></a>24.3&nbsp;Adding a Message Handler, Building, and Running</h2></div></div></div><p>Modify the <code class="literal">com.example.loggingconsumer.LoggingConsumerApplication</code> class to look as follows:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoggingConsumerApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(LoggingConsumerApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handle(Person person) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Received: "</span> + person);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Person {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> String name;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String getName() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> name;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setName(String name) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.name = name;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String toString() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.name;
}
}
}</pre><p>As you can see from the preceding listing:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">We have enabled <code class="literal">Sink</code> binding (input-no-output) by using <code class="literal">@EnableBinding(Sink.class)</code>.
Doing so signals to the framework to initiate binding to the messaging middleware, where it automatically creates the destination (that is, queue, topic, and others) that are bound to the <code class="literal">Sink.INPUT</code> channel.</li><li class="listitem">We have added a <code class="literal">handler</code> method to receive incoming messages of type <code class="literal">Person</code>.
Doing so lets you see one of the core features of the framework: It tries to automatically convert incoming message payloads to type <code class="literal">Person</code>.</li></ul></div><p>You now have a fully functional Spring Cloud Stream application that does listens for messages.
From here, for simplicity, we assume you selected RabbitMQ in <a class="link" href="#spring-cloud-stream-preface-creating-sample-application" title="24.1&nbsp;Creating a Sample Application by Using Spring Initializr">step one</a>.
Assuming you have RabbitMQ installed and running, you can start the application by running its <code class="literal">main</code> method in your IDE.</p><p>You should see following output:</p><pre class="screen"> --- [ main] c.s.b.r.p.RabbitExchangeQueueProvisioner : declaring queue for inbound: input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg, bound to: input
--- [ main] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [localhost:5672]
--- [ main] o.s.a.r.c.CachingConnectionFactory : Created new connection: rabbitConnectionFactory#2a3a299:0/SimpleConnection@66c83fc8. . .
. . .
--- [ main] o.s.i.a.i.AmqpInboundChannelAdapter : started inbound.input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg
. . .
--- [ main] c.e.l.LoggingConsumerApplication : Started LoggingConsumerApplication in 2.531 seconds (JVM running for 2.897)</pre><p>Go to the RabbitMQ management console or any other RabbitMQ client and send a message to <code class="literal">input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg</code>.
The <code class="literal">anonymous.CbMIwdkJSBO1ZoPDOtHtCg</code> part represents the group name and is generated, so it is bound to be different in your environment.
For something more predictable, you can use an explicit group name by setting <code class="literal">spring.cloud.stream.bindings.input.group=hello</code> (or whatever name you like).</p><p>The contents of the message should be a JSON representation of the <code class="literal">Person</code> class, as follows:</p><pre class="literallayout">{"name":"Sam Spade"}</pre><p>Then, in your console, you should see:</p><p><code class="literal">Received: Sam Spade</code></p><p>You can also build and package your application into a boot jar (by using <code class="literal">./mvnw clean install</code>) and run the built JAR by using the <code class="literal">java -jar</code> command.</p><p>Now you have a working (albeit very basic) Spring Cloud Stream application.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_what_s_new_in_2_0" href="#_what_s_new_in_2_0"></a>25.&nbsp;What&#8217;s New in 2.0?</h2></div></div></div><p>Spring Cloud Stream introduces a number of new features, enhancements, and changes. The following sections outline the most notable ones:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-new-features" title="25.1&nbsp;New Features and Components">Section&nbsp;25.1, &#8220;New Features and Components&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-notable-enhancements" title="25.2&nbsp;Notable Enhancements">Section&nbsp;25.2, &#8220;Notable Enhancements&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-preface-new-features" href="#spring-cloud-stream-preface-new-features"></a>25.1&nbsp;New Features and Components</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>Polling Consumers</strong></span>: Introduction of polled consumers, which lets the application control message processing rates.
See &#8220;<a class="xref" href="#spring-cloud-streams-overview-using-polled-consumers" title="28.3.5&nbsp;Using Polled Consumers">Section&nbsp;28.3.5, &#8220;Using Polled Consumers&#8221;</a>&#8221; for more details.
You can also read <a class="link" href="https://spring.io/blog/2018/02/27/spring-cloud-stream-2-0-polled-consumers" target="_top">this blog post</a> for more details.</li><li class="listitem"><span class="strong"><strong>Micrometer Support</strong></span>: Metrics has been switched to use <a class="link" href="https://micrometer.io/" target="_top">Micrometer</a>.
<code class="literal">MeterRegistry</code> is also provided as a bean so that custom applications can autowire it to capture custom metrics.
See &#8220;<a class="xref" href="#spring-cloud-stream-overview-metrics-emitter" title="36.&nbsp;Metrics Emitter">Chapter&nbsp;36, <i>Metrics Emitter</i></a>&#8221; for more details.</li><li class="listitem"><span class="strong"><strong>New Actuator Binding Controls</strong></span>: New actuator binding controls let you both visualize and control the Bindings lifecycle.
For more details, see <a class="xref" href="#_binding_visualization_and_control" title="29.6&nbsp;Binding visualization and control">Section&nbsp;29.6, &#8220;Binding visualization and control&#8221;</a>.</li><li class="listitem"><span class="strong"><strong>Configurable RetryTemplate</strong></span>: Aside from providing properties to configure <code class="literal">RetryTemplate</code>, we now let you provide your own template, effectively overriding the one provided by the framework.
To use it, configure it as a <code class="literal">@Bean</code> in your application.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-preface-notable-enhancements" href="#spring-cloud-stream-preface-notable-enhancements"></a>25.2&nbsp;Notable Enhancements</h2></div></div></div><p>This version includes the following notable enhancements:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-actuator-web-dependencies" title="25.2.1&nbsp;Both Actuator and Web Dependencies Are Now Optional">Section&nbsp;25.2.1, &#8220;Both Actuator and Web Dependencies Are Now Optional&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-content-type-negotiation-improvements" title="25.2.2&nbsp;Content-type Negotiation Improvements">Section&nbsp;25.2.2, &#8220;Content-type Negotiation Improvements&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-notable-deprecations" title="25.3&nbsp;Notable Deprecations">Section&nbsp;25.3, &#8220;Notable Deprecations&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-stream-preface-actuator-web-dependencies" href="#spring-cloud-stream-preface-actuator-web-dependencies"></a>25.2.1&nbsp;Both Actuator and Web Dependencies Are Now Optional</h3></div></div></div><p>This change slims down the footprint of the deployed application in the event neither actuator nor web dependencies required.
It also lets you switch between the reactive and conventional web paradigms by manually adding one of the following dependencies.</p><p>The following listing shows how to add the conventional web framework:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-web<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>The following listing shows how to add the reactive web framework:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-webflux<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>The following list shows how to add the actuator dependency:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-actuator<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-stream-preface-content-type-negotiation-improvements" href="#spring-cloud-stream-preface-content-type-negotiation-improvements"></a>25.2.2&nbsp;Content-type Negotiation Improvements</h3></div></div></div><p>One of the core themes for verion 2.0 is improvements (in both consistency and performance) around content-type negotiation and message conversion.
The following summary outlines the notable changes and improvements in this area.
See the &#8220;<a class="xref" href="#content-type-management" title="31.&nbsp;Content Type Negotiation">Chapter&nbsp;31, <i>Content Type Negotiation</i></a>&#8221; section for more details.
Also <a class="link" href="https://spring.io/blog/2018/02/26/spring-cloud-stream-2-0-content-type-negotiation-and-transformation" target="_top">this blog post</a> contains more detail.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">All message conversion is now handled <span class="strong"><strong>only</strong></span> by <code class="literal">MessageConverter</code> objects.</li><li class="listitem">We introduced the <code class="literal">@StreamMessageConverter</code> annotation to provide custom <code class="literal">MessageConverter</code> objects.</li><li class="listitem">We introduced the default <code class="literal">Content Type</code> as <code class="literal">application/json</code>, which needs to be taken into consideration when migrating 1.3 application or operating in the mixed mode (that is, 1.3 producer &#8594; 2.0 consumer).</li><li class="listitem">Messages with textual payloads and a <code class="literal">contentType</code> of <code class="literal">text/&#8230;&#8203;</code> or <code class="literal">&#8230;&#8203;/json</code> are no longer converted to <code class="literal">Message&lt;String&gt;</code> for cases where the argument type of the provided <code class="literal">MessageHandler</code> can not be determined (that is, <code class="literal">public void handle(Message&lt;?&gt; message)</code> or <code class="literal">public void handle(Object payload)</code>).
Furthermore, a strong argument type may not be enough to properly convert messages, so the <code class="literal">contentType</code> header may be used as a supplement by some <code class="literal">MessageConverters</code>.</li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-preface-notable-deprecations" href="#spring-cloud-stream-preface-notable-deprecations"></a>25.3&nbsp;Notable Deprecations</h2></div></div></div><p>As of version 2.0, the following items have been deprecated:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-deprecation-java-serialization" title="25.3.1&nbsp;Java Serialization (Java Native and Kryo)">Section&nbsp;25.3.1, &#8220;Java Serialization (Java Native and Kryo)&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-stream-preface-deprecation-classes-methods" title="25.3.2&nbsp;Deprecated Classes and Methods">Section&nbsp;25.3.2, &#8220;Deprecated Classes and Methods&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-stream-preface-deprecation-java-serialization" href="#spring-cloud-stream-preface-deprecation-java-serialization"></a>25.3.1&nbsp;Java Serialization (Java Native and Kryo)</h3></div></div></div><p><code class="literal">JavaSerializationMessageConverter</code> and <code class="literal">KryoMessageConverter</code> remain for now. However, we plan to move them out of the core packages and support in the future.
The main reason for this deprecation is to flag the issue that type-based, language-specific serialization could cause in distributed environments, where Producers and Consumers may depend on different JVM versions or have different versions of supporting libraries (that is, Kryo).
We also wanted to draw the attention to the fact that Consumers and Producers may not even be Java-based, so polyglot style serialization (i.e., JSON) is better suited.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-stream-preface-deprecation-classes-methods" href="#spring-cloud-stream-preface-deprecation-classes-methods"></a>25.3.2&nbsp;Deprecated Classes and Methods</h3></div></div></div><p>The following is a quick summary of notable deprecations. See the corresponding {spring-cloud-stream-javadoc-current}[javadoc] for more details.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">SharedChannelRegistry</code>. Use <code class="literal">SharedBindingTargetRegistry</code>.</li><li class="listitem"><code class="literal">Bindings</code>.
Beans qualified by it are already uniquely identified by their type&#8201;&#8212;&#8201;for example, provided <code class="literal">Source</code>, <code class="literal">Processor</code>, or custom bindings:</li></ul></div><pre class="screen">public interface Sample {
String OUTPUT = "sampleOutput";
@Output(Sample.OUTPUT)
MessageChannel output();
}</pre><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">HeaderMode.raw</code>. Use <code class="literal">none</code>, <code class="literal">headers</code> or <code class="literal">embeddedHeaders</code></li><li class="listitem"><code class="literal">ProducerProperties.partitionKeyExtractorClass</code> in favor of <code class="literal">partitionKeyExtractorName</code> and <code class="literal">ProducerProperties.partitionSelectorClass</code> in favor of <code class="literal">partitionSelectorName</code>.
This change ensures that both components are Spring configured and managed and are referenced in a Spring-friendly way.</li><li class="listitem"><code class="literal">BinderAwareRouterBeanPostProcessor</code>. While the component remains, it is no longer a <code class="literal">BeanPostProcessor</code> and will be renamed in the future.</li><li class="listitem"><code class="literal">BinderProperties.setEnvironment(Properties environment)</code>. Use <code class="literal">BinderProperties.setEnvironment(Map&lt;String, Object&gt; environment)</code>.</li></ul></div><p>This section goes into more detail about how you can work with Spring Cloud Stream.
It covers topics such as creating and running stream applications.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-stream-overview-introducing" href="#spring-cloud-stream-overview-introducing"></a>26.&nbsp;Introducing Spring Cloud Stream</h2></div></div></div><p>Spring Cloud Stream is a framework for building message-driven microservice applications.
Spring Cloud Stream builds upon Spring Boot to create standalone, production-grade Spring applications and uses Spring Integration to provide connectivity to message brokers.
It provides opinionated configuration of middleware from several vendors, introducing the concepts of persistent publish-subscribe semantics, consumer groups, and partitions.</p><p>You can add the <code class="literal">@EnableBinding</code> annotation to your application to get immediate connectivity to a message broker, and you can add <code class="literal">@StreamListener</code> to a method to cause it to receive events for stream processing.
The following example shows a sink application that receives external messages:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> VoteRecordingSinkApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(VoteRecordingSinkApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> processVote(Vote vote) {
votingService.recordVote(vote);
}
}</pre><p>The <code class="literal">@EnableBinding</code> annotation takes one or more interfaces as parameters (in this case, the parameter is a single <code class="literal">Sink</code> interface).
An interface declares input and output channels.
Spring Cloud Stream provides the <code class="literal">Source</code>, <code class="literal">Sink</code>, and <code class="literal">Processor</code> interfaces. You can also define your own interfaces.</p><p>The following listing shows the definition of the <code class="literal">Sink</code> interface:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> Sink {
String INPUT = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"input"</span>;
<em><span class="hl-annotation" style="color: gray">@Input(Sink.INPUT)</span></em>
SubscribableChannel input();
}</pre><p>The <code class="literal">@Input</code> annotation identifies an input channel, through which received messages enter the application.
The <code class="literal">@Output</code> annotation identifies an output channel, through which published messages leave the application.
The <code class="literal">@Input</code> and <code class="literal">@Output</code> annotations can take a channel name as a parameter.
If a name is not provided, the name of the annotated method is used.</p><p>Spring Cloud Stream creates an implementation of the interface for you.
You can use this in the application by autowiring it, as shown in the following example (from a test case):</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringJUnit4ClassRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringApplicationConfiguration(classes = VoteRecordingSinkApplication.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@WebAppConfiguration</span></em>
<em><span class="hl-annotation" style="color: gray">@DirtiesContext</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StreamApplicationTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Sink sink;
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() {
assertNotNull(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.sink.input());
}
}</pre></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_main_concepts" href="#_main_concepts"></a>27.&nbsp;Main Concepts</h2></div></div></div><p>Spring Cloud Stream provides a number of abstractions and primitives that simplify the writing of message-driven microservice applications.
This section gives an overview of the following:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="#spring-cloud-stream-overview-application-model" title="27.1&nbsp;Application Model">Spring Cloud Stream&#8217;s application model</a></li><li class="listitem"><a class="xref" href="#spring-cloud-stream-overview-binder-abstraction" title="27.2&nbsp;The Binder Abstraction">Section&nbsp;27.2, &#8220;The Binder Abstraction&#8221;</a></li><li class="listitem"><a class="link" href="#spring-cloud-stream-overview-persistent-publish-subscribe-support" title="27.3&nbsp;Persistent Publish-Subscribe Support">Persistent publish-subscribe support</a></li><li class="listitem"><a class="link" href="#consumer-groups" title="27.4&nbsp;Consumer Groups">Consumer group support</a></li><li class="listitem"><a class="link" href="#partitioning" title="27.6&nbsp;Partitioning Support">Partitioning support</a></li><li class="listitem"><a class="link" href="#spring-cloud-stream-overview-binder-api" title="29.2&nbsp;Binder SPI">A pluggable Binder SPI</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-application-model" href="#spring-cloud-stream-overview-application-model"></a>27.1&nbsp;Application Model</h2></div></div></div><p>A Spring Cloud Stream application consists of a middleware-neutral core.
The application communicates with the outside world through input and output channels injected into it by Spring Cloud Stream.
Channels are connected to external brokers through middleware-specific Binder implementations.</p><div class="figure"><a name="d0e7780" href="#d0e7780"></a><p class="title"><b>Figure&nbsp;27.1.&nbsp;Spring Cloud Stream Application</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/SCSt-with-binder.png" align="middle" alt="SCSt with binder"></div></div></div><br class="figure-break"><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_fat_jar" href="#_fat_jar"></a>27.1.1&nbsp;Fat JAR</h3></div></div></div><p>Spring Cloud Stream applications can be run in stand-alone mode from your IDE for testing.
To run a Spring Cloud Stream application in production, you can create an executable (or &#8220;fat&#8221;) JAR by using the standard Spring Boot tooling provided for Maven or Gradle. See the <a class="link" href="https://docs.spring.io/spring-boot/docs/current/reference/html/howto-build.html#howto-create-an-executable-jar-with-maven" target="_top">Spring Boot Reference Guide</a> for more details.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-binder-abstraction" href="#spring-cloud-stream-overview-binder-abstraction"></a>27.2&nbsp;The Binder Abstraction</h2></div></div></div><p>Spring Cloud Stream provides Binder implementations for <a class="link" href="https://github.com/spring-cloud/spring-cloud-stream-binder-kafka" target="_top">Kafka</a> and <a class="link" href="https://github.com/spring-cloud/spring-cloud-stream-binder-rabbit" target="_top">Rabbit MQ</a>.
Spring Cloud Stream also includes a <a class="link" href="https://github.com/spring-cloud/spring-cloud-stream/blob/master/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/TestSupportBinder.java" target="_top">TestSupportBinder</a>, which leaves a channel unmodified so that tests can interact with channels directly and reliably assert on what is received.
You can also use the extensible API to write your own Binder.</p><p>Spring Cloud Stream uses Spring Boot for configuration, and the Binder abstraction makes it possible for a Spring Cloud Stream application to be flexible in how it connects to middleware.
For example, deployers can dynamically choose, at runtime, the destinations (such as the Kafka topics or RabbitMQ exchanges) to which channels connect.
Such configuration can be provided through external configuration properties and in any form supported by Spring Boot (including application arguments, environment variables, and <code class="literal">application.yml</code> or <code class="literal">application.properties</code> files).
In the sink example from the <a class="xref" href="#spring-cloud-stream-overview-introducing" title="26.&nbsp;Introducing Spring Cloud Stream">Chapter&nbsp;26, <i>Introducing Spring Cloud Stream</i></a> section, setting the <code class="literal">spring.cloud.stream.bindings.input.destination</code> application property to <code class="literal">raw-sensor-data</code> causes it to read from the <code class="literal">raw-sensor-data</code> Kafka topic or from a queue bound to the <code class="literal">raw-sensor-data</code> RabbitMQ exchange.</p><p>Spring Cloud Stream automatically detects and uses a binder found on the classpath.
You can use different types of middleware with the same code.
To do so, include a different binder at build time.
For more complex use cases, you can also package multiple binders with your application and have it choose the binder( and even whether to use different binders for different channels) at runtime.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-persistent-publish-subscribe-support" href="#spring-cloud-stream-overview-persistent-publish-subscribe-support"></a>27.3&nbsp;Persistent Publish-Subscribe Support</h2></div></div></div><p>Communication between applications follows a publish-subscribe model, where data is broadcast through shared topics.
This can be seen in the following figure, which shows a typical deployment for a set of interacting Spring Cloud Stream applications.</p><div class="figure"><a name="d0e7840" href="#d0e7840"></a><p class="title"><b>Figure&nbsp;27.2.&nbsp;Spring Cloud Stream Publish-Subscribe</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/SCSt-sensors.png" align="middle" alt="SCSt sensors"></div></div></div><br class="figure-break"><p>Data reported by sensors to an HTTP endpoint is sent to a common destination named <code class="literal">raw-sensor-data</code>.
From the destination, it is independently processed by a microservice application that computes time-windowed averages and by another microservice application that ingests the raw data into HDFS (Hadoop Distributed File System).
In order to process the data, both applications declare the topic as their input at runtime.</p><p>The publish-subscribe communication model reduces the complexity of both the producer and the consumer and lets new applications be added to the topology without disruption of the existing flow.
For example, downstream from the average-calculating application, you can add an application that calculates the highest temperature values for display and monitoring.
You can then add another application that interprets the same flow of averages for fault detection.
Doing all communication through shared topics rather than point-to-point queues reduces coupling between microservices.</p><p>While the concept of publish-subscribe messaging is not new, Spring Cloud Stream takes the extra step of making it an opinionated choice for its application model.
By using native middleware support, Spring Cloud Stream also simplifies use of the publish-subscribe model across different platforms.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="consumer-groups" href="#consumer-groups"></a>27.4&nbsp;Consumer Groups</h2></div></div></div><p>While the publish-subscribe model makes it easy to connect applications through shared topics, the ability to scale up by creating multiple instances of a given application is equally important.
When doing so, different instances of an application are placed in a competing consumer relationship, where only one of the instances is expected to handle a given message.</p><p>Spring Cloud Stream models this behavior through the concept of a consumer group.
(Spring Cloud Stream consumer groups are similar to and inspired by Kafka consumer groups.)
Each consumer binding can use the <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.group</code> property to specify a group name.
For the consumers shown in the following figure, this property would be set as <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.group=hdfsWrite</code> or <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.group=average</code>.</p><div class="figure"><a name="d0e7874" href="#d0e7874"></a><p class="title"><b>Figure&nbsp;27.3.&nbsp;Spring Cloud Stream Consumer Groups</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/SCSt-groups.png" align="middle" alt="SCSt groups"></div></div></div><br class="figure-break"><p>All groups that subscribe to a given destination receive a copy of published data, but only one member of each group receives a given message from that destination.
By default, when a group is not specified, Spring Cloud Stream assigns the application to an anonymous and independent single-member consumer group that is in a publish-subscribe relationship with all other consumer groups.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="consumer-types" href="#consumer-types"></a>27.5&nbsp;Consumer Types</h2></div></div></div><p>Two types of consumer are supported:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Message-driven (sometimes referred to as Asynchronous)</li><li class="listitem">Polled (sometimes referred to as Synchronous)</li></ul></div><p>Prior to version 2.0, only asynchronous consumers were supported. A message is delivered as soon as it is available and a thread is available to process it.</p><p>When you wish to control the rate at which messages are processed, you might want to use a synchronous consumer.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="durability" href="#durability"></a>27.5.1&nbsp;Durability</h3></div></div></div><p>Consistent with the opinionated application model of Spring Cloud Stream, consumer group subscriptions are durable.
That is, a binder implementation ensures that group subscriptions are persistent and that, once at least one subscription for a group has been created, the group receives messages, even if they are sent while all applications in the group are stopped.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Anonymous subscriptions are non-durable by nature.
For some binder implementations (such as RabbitMQ), it is possible to have non-durable group subscriptions.</p></td></tr></table></div><p>In general, it is preferable to always specify a consumer group when binding an application to a given destination.
When scaling up a Spring Cloud Stream application, you must specify a consumer group for each of its input bindings.
Doing so prevents the application&#8217;s instances from receiving duplicate messages (unless that behavior is desired, which is unusual).</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="partitioning" href="#partitioning"></a>27.6&nbsp;Partitioning Support</h2></div></div></div><p>Spring Cloud Stream provides support for partitioning data between multiple instances of a given application.
In a partitioned scenario, the physical communication medium (such as the broker topic) is viewed as being structured into multiple partitions.
One or more producer application instances send data to multiple consumer application instances and ensure that data identified by common characteristics are processed by the same consumer instance.</p><p>Spring Cloud Stream provides a common abstraction for implementing partitioned processing use cases in a uniform fashion.
Partitioning can thus be used whether the broker itself is naturally partitioned (for example, Kafka) or not (for example, RabbitMQ).</p><div class="figure"><a name="d0e7918" href="#d0e7918"></a><p class="title"><b>Figure&nbsp;27.4.&nbsp;Spring Cloud Stream Partitioning</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/SCSt-partitioning.png" align="middle" alt="SCSt partitioning"></div></div></div><br class="figure-break"><p>Partitioning is a critical concept in stateful processing, where it is critical (for either performance or consistency reasons) to ensure that all related data is processed together.
For example, in the time-windowed average calculation example, it is important that all measurements from any given sensor are processed by the same application instance.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>To set up a partitioned processing scenario, you must configure both the data-producing and the data-consuming ends.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_programming_model" href="#_programming_model"></a>28.&nbsp;Programming Model</h2></div></div></div><p>To understand the programming model, you should be familiar with the following core concepts:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>Destination Binders:</strong></span> Components responsible to provide integration with the external messaging systems.</li><li class="listitem"><span class="strong"><strong>Destination Bindings:</strong></span> Bridge between the external messaging systems and application provided <span class="emphasis"><em>Producers</em></span> and <span class="emphasis"><em>Consumers</em></span> of messages (created by the Destination Binders).</li><li class="listitem"><span class="strong"><strong>Message:</strong></span> The canonical data structure used by producers and consumers to communicate with Destination Binders (and thus other applications via external messaging systems).</li></ul></div><div class="informalfigure"><div class="mediaobject" align="center"><img src="images/SCSt-overview.png" align="middle" alt="SCSt overview"></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_destination_binders" href="#_destination_binders"></a>28.1&nbsp;Destination Binders</h2></div></div></div><p>Destination Binders are extension components of Spring Cloud Stream responsible for providing the necessary configuration and implementation to facilitate
integration with external messaging systems.
This integration is responsible for connectivity, delegation, and routing of messages to and from producers and consumers, data type conversion,
invocation of the user code, and more.</p><p>Binders handle a lot of the boiler plate responsibilities that would otherwise fall on your shoulders. However, to accomplish that, the binder still needs
some help in the form of minimalistic yet required set of instructions from the user, which typically come in the form of some type of configuration.</p><p>While it is out of scope of this section to discuss all of the available binder and binding configuration options (the rest of the manual covers them extensively),
<span class="emphasis"><em>Destination Binding</em></span> does require special attention. The next section discusses it in detail.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_destination_bindings" href="#_destination_bindings"></a>28.2&nbsp;Destination Bindings</h2></div></div></div><p>As stated earlier, <span class="emphasis"><em>Destination Bindings</em></span> provide a bridge between the external messaging system and application-provided <span class="emphasis"><em>Producers</em></span> and <span class="emphasis"><em>Consumers</em></span>.</p><p>Applying the @EnableBinding annotation to one of the application&#8217;s configuration classes defines a destination binding.
The <code class="literal">@EnableBinding</code> annotation itself is meta-annotated with <code class="literal">@Configuration</code> and triggers the configuration of the Spring Cloud Stream infrastructure.</p><p>The following example shows a fully configured and functioning Spring Cloud Stream application that receives the payload of the message from the <code class="literal">INPUT</code>
destination as a <code class="literal">String</code> type (see <a class="xref" href="#content-type-management" title="31.&nbsp;Content Type Negotiation">Chapter&nbsp;31, <i>Content Type Negotiation</i></a> section), logs it to the console and sends it to the <code class="literal">OUTPUT</code> destination after converting it to upper case.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(MyApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Processor.INPUT)</span></em>
<em><span class="hl-annotation" style="color: gray">@SendTo(Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String handle(String value) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Received: "</span> + value);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> value.toUpperCase();
}
}</pre><p>As you can see the <code class="literal">@EnableBinding</code> annotation can take one or more interface classes as parameters. The parameters are referred to as <span class="emphasis"><em>bindings</em></span>,
and they contain methods representing <span class="emphasis"><em>bindable components</em></span>.
These components are typically message channels (see <a class="link" href="https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-messaging.html" target="_top">Spring Messaging</a>)
for channel-based binders (such as Rabbit, Kafka, and others). However other types of bindings can
provide support for the native features of the corresponding technology. For example Kafka Streams binder (formerly known as KStream) allows native bindings directly to Kafka Streams
(see <a class="link" href="https://docs.spring.io/autorepo/docs/spring-cloud-stream-binder-kafka-docs/1.1.0.M1/reference/htmlsingle/" target="_top">Kafka Streams</a> for more details).</p><p>Spring Cloud Stream already provides <span class="emphasis"><em>binding</em></span> interfaces for typical message exchange contracts, which include:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>Sink:</strong></span> Identifies the contract for the message consumer by providing the destination from which the message is consumed.</li><li class="listitem"><span class="strong"><strong>Source:</strong></span> Identifies the contract for the message producer by providing the destination to which the produced message is sent.</li><li class="listitem"><span class="strong"><strong>Processor:</strong></span> Encapsulates both the sink and the source contracts by exposing two destinations that allow consumption and production of messages.</li></ul></div><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> Sink {
String INPUT = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"input"</span>;
<em><span class="hl-annotation" style="color: gray">@Input(Sink.INPUT)</span></em>
SubscribableChannel input();
}</pre><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> Source {
String OUTPUT = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"output"</span>;
<em><span class="hl-annotation" style="color: gray">@Output(Source.OUTPUT)</span></em>
MessageChannel output();
}</pre><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> Processor <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Source, Sink {}</pre><p>While the preceding example satisfies the majority of cases, you can also define your own contracts by defining your own bindings interfaces and use <code class="literal">@Input</code> and <code class="literal">@Output</code>
annotations to identify the actual <span class="emphasis"><em>bindable components</em></span>.</p><p>For example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> Barista {
<em><span class="hl-annotation" style="color: gray">@Input</span></em>
SubscribableChannel orders();
<em><span class="hl-annotation" style="color: gray">@Output</span></em>
MessageChannel hotDrinks();
<em><span class="hl-annotation" style="color: gray">@Output</span></em>
MessageChannel coldDrinks();
}</pre><p>Using the interface shown in the preceding example as a parameter to <code class="literal">@EnableBinding</code> triggers the creation of the three bound channels named <code class="literal">orders</code>, <code class="literal">hotDrinks</code>, and <code class="literal">coldDrinks</code>,
respectively.</p><p>You can provide as many binding interfaces as you need, as arguments to the <code class="literal">@EnableBinding</code> annotation, as shown in the following example:</p><pre class="programlisting">@EnableBinding(value = { Orders.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, Payment.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> })</pre><p>In Spring Cloud Stream, the bindable <code class="literal">MessageChannel</code> components are the Spring Messaging <code class="literal">MessageChannel</code> (for outbound) and its extension, <code class="literal">SubscribableChannel</code>,
(for inbound).</p><p><span class="strong"><strong>Pollable Destination Binding</strong></span></p><p>While the previously described bindings support event-based message consumption, sometimes you need more control, such as rate of consumption.</p><p>Starting with version 2.0, you can now bind a pollable consumer:</p><p>The following example shows how to bind a pollable consumer:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> PolledBarista {
<em><span class="hl-annotation" style="color: gray">@Input</span></em>
PollableMessageSource orders();
. . .
}</pre><p>In this case, an implementation of <code class="literal">PollableMessageSource</code> is bound to the <code class="literal">orders</code> &#8220;channel&#8221;. See <a class="xref" href="#spring-cloud-streams-overview-using-polled-consumers" title="28.3.5&nbsp;Using Polled Consumers">Section&nbsp;28.3.5, &#8220;Using Polled Consumers&#8221;</a> for more details.</p><p><span class="strong"><strong>Customizing Channel Names</strong></span></p><p>By using the <code class="literal">@Input</code> and <code class="literal">@Output</code> annotations, you can specify a customized channel name for the channel, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> Barista {
<em><span class="hl-annotation" style="color: gray">@Input("inboundOrders")</span></em>
SubscribableChannel orders();
}</pre><p>In the preceding example, the created bound channel is named <code class="literal">inboundOrders</code>.</p><p>Normally, you need not access individual channels or bindings directly (other then configuring them via <code class="literal">@EnableBinding</code> annotation). However there may be
times, such as testing or other corner cases, when you do.</p><p>Aside from generating channels for each binding and registering them as Spring beans, for each bound interface, Spring Cloud Stream generates a bean that implements the interface.
That means you can have access to the interfaces representing the bindings or individual channels by auto-wiring either in your application, as shown in the following two examples:</p><p><span class="emphasis"><em>Autowire Binding interface</em></span></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowire</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Source source
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> sayHello(String name) {
source.output().send(MessageBuilder.withPayload(name).build());
}</pre><p><span class="emphasis"><em>Autowire individual channel</em></span></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowire</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MessageChannel output;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> sayHello(String name) {
output.send(MessageBuilder.withPayload(name).build());
}</pre><p>You can also use standard Spring&#8217;s <code class="literal">@Qualifier</code> annotation for cases when channel names are customized or in multiple-channel scenarios that require specifically named channels.</p><p>The following example shows how to use the @Qualifier annotation in this way:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowire</span></em>
<em><span class="hl-annotation" style="color: gray">@Qualifier("myChannel")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MessageChannel output;</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-producing-consuming-messages" href="#spring-cloud-stream-overview-producing-consuming-messages"></a>28.3&nbsp;Producing and Consuming Messages</h2></div></div></div><p>You can write a Spring Cloud Stream application by using either Spring Integration annotations or Spring Cloud Stream native annotation.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_integration_support" href="#_spring_integration_support"></a>28.3.1&nbsp;Spring Integration Support</h3></div></div></div><p>Spring Cloud Stream is built on the concepts and patterns defined by <a class="link" href="http://www.enterpriseintegrationpatterns.com/" target="_top">Enterprise Integration Patterns</a> and relies
in its internal implementation on an already established and popular implementation of Enterprise Integration Patterns within the Spring portfolio of projects:
<a class="link" href="https://projects.spring.io/spring-integration/" target="_top">Spring Integration</a> framework.</p><p>So its only natural for it to support the foundation, semantics, and configuration options that are already established by Spring Integration</p><p>For example, you can attach the output channel of a <code class="literal">Source</code> to a <code class="literal">MessageSource</code> and use the familiar <code class="literal">@InboundChannelAdapter</code> annotation, as follows:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> TimerSource {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@InboundChannelAdapter(value = Source.OUTPUT, poller = @Poller(fixedDelay = "10", maxMessagesPerPoll = "1"))</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> MessageSource&lt;String&gt; timerMessageSource() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> () -&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> GenericMessage&lt;&gt;(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello Spring Cloud Stream"</span>);
}
}</pre><p>Similarly, you can use @Transformer or @ServiceActivator while providing an implementation of a message handler method for a <span class="emphasis"><em>Processor</em></span> binding contract, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> TransformProcessor {
<em><span class="hl-annotation" style="color: gray">@Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Object transform(String message) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> message.toUpperCase();
}
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>While this may be skipping ahead a bit, it is important to understand that, when you consume from the same binding using <code class="literal">@StreamListener</code> annotation, a pub-sub model is used.
Each method annotated with <code class="literal">@StreamListener</code> receives its own copy of a message, and each one has its own consumer group.
However, if you consume from the same binding by using one of the Spring Integration annotation (such as <code class="literal">@Aggregator</code>, <code class="literal">@Transformer</code>, or <code class="literal">@ServiceActivator</code>), those consume in a competing model.
No individual consumer group is created for each subscription.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_streamlistener_annotation" href="#_using_streamlistener_annotation"></a>28.3.2&nbsp;Using @StreamListener Annotation</h3></div></div></div><p>Complementary to its Spring Integration support, Spring Cloud Stream provides its own <code class="literal">@StreamListener</code> annotation, modeled after other Spring Messaging annotations
(<code class="literal">@MessageMapping</code>, <code class="literal">@JmsListener</code>, <code class="literal">@RabbitListener</code>, and others) and provides conviniences, such as content-based routing and others.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> VoteHandler {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
VotingService votingService;
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handle(Vote vote) {
votingService.record(vote);
}
}</pre><p>As with other Spring Messaging methods, method arguments can be annotated with <code class="literal">@Payload</code>, <code class="literal">@Headers</code>, and <code class="literal">@Header</code>.</p><p>For methods that return data, you must use the <code class="literal">@SendTo</code> annotation to specify the output binding destination for data returned by the method, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> TransformProcessor {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
VotingService votingService;
<em><span class="hl-annotation" style="color: gray">@StreamListener(Processor.INPUT)</span></em>
<em><span class="hl-annotation" style="color: gray">@SendTo(Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> VoteResult handle(Vote vote) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> votingService.record(vote);
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_streamlistener_for_content_based_routing" href="#_using_streamlistener_for_content_based_routing"></a>28.3.3&nbsp;Using @StreamListener for Content-based routing</h3></div></div></div><p>Spring Cloud Stream supports dispatching messages to multiple handler methods annotated with <code class="literal">@StreamListener</code> based on conditions.</p><p>In order to be eligible to support conditional dispatching, a method must satisfy the follow conditions:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">It must not return a value.</li><li class="listitem">It must be an individual message handling method (reactive API methods are not supported).</li></ul></div><p>The condition is specified by a SpEL expression in the <code class="literal">condition</code> argument of the annotation and is evaluated for each message.
All the handlers that match the condition are invoked in the same thread, and no assumption must be made about the order in which the invocations take place.</p><p>In the following example of a <code class="literal">@StreamListener</code> with dispatching conditions, all the messages bearing a header <code class="literal">type</code> with the value <code class="literal">bogey</code> are dispatched to the
<code class="literal">receiveBogey</code> method, and all the messages bearing a header <code class="literal">type</code> with the value <code class="literal">bacall</code> are dispatched to the <code class="literal">receiveBacall</code> method.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> TestPojoWithAnnotatedArguments {
<em><span class="hl-annotation" style="color: gray">@StreamListener(target = Sink.INPUT, condition = "headers['type']=='bogey'")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> receiveBogey(<em><span class="hl-annotation" style="color: gray">@Payload</span></em> BogeyPojo bogeyPojo) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// handle the message</span>
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(target = Sink.INPUT, condition = "headers['type']=='bacall'")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> receiveBacall(<em><span class="hl-annotation" style="color: gray">@Payload</span></em> BacallPojo bacallPojo) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// handle the message</span>
}
}</pre><p><span class="strong"><strong>Content Type Negotiation in the Context of <code class="literal">condition</code></strong></span></p><p>It is important to understand some of the mechanics behind content-based routing using the <code class="literal">condition</code> argument of <code class="literal">@StreamListener</code>, especially in the context of the type of the message as a whole.
It may also help if you familiarize yourself with the <a class="xref" href="#content-type-management" title="31.&nbsp;Content Type Negotiation">Chapter&nbsp;31, <i>Content Type Negotiation</i></a> before you proceed.</p><p>Consider the following scenario:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CatsAndDogs {
<em><span class="hl-annotation" style="color: gray">@StreamListener(target = Sink.INPUT, condition = "payload.class.simpleName=='Dog'")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> bark(Dog dog) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// handle the message</span>
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(target = Sink.INPUT, condition = "payload.class.simpleName=='Cat'")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> purr(Cat cat) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// handle the message</span>
}
}</pre><p>The preceding code is perfectly valid. It compiles and deploys without any issues, yet it never produces the result you expect.</p><p>That is because you are testing something that does not yet exist in a state you expect. That is because the payload of the message is not yet converted from the
wire format (<code class="literal">byte[]</code>) to the desired type.
In other words, it has not yet gone through the type conversion process described in the <a class="xref" href="#content-type-management" title="31.&nbsp;Content Type Negotiation">Chapter&nbsp;31, <i>Content Type Negotiation</i></a>.</p><p>So, unless you use a SPeL expression that evaluates raw data (for example, the value of the first byte in the byte array), use message header-based expressions
(such as <code class="literal">condition = "headers['type']=='dog'"</code>).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>At the moment, dispatching through <code class="literal">@StreamListener</code> conditions is supported only for channel-based binders (not for reactive programming)
support.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_cloud_function" href="#_spring_cloud_function"></a>28.3.4&nbsp;Spring Cloud Function support</h3></div></div></div><p>Since Spring Cloud Stream v2.1, another alternative for defining <span class="emphasis"><em>stream handlers</em></span> and <span class="emphasis"><em>sources</em></span> is to use build-in
support for <a class="link" href="https://cloud.spring.io/spring-cloud-function/" target="_top">Spring Cloud Function</a> where they can be expressed as beans of
type <code class="literal">java.util.function.[Supplier/Function/Consumer]</code>.</p><p>To specify which functional bean to bind to the external destination(s) exposed by the bindings, you must provide <code class="literal">spring.cloud.stream.function.definition</code> property.</p><p>Here is the example of the Processor application exposing message handler as <code class="literal">java.util.function.Function</code></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyFunctionBootApp {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(MyFunctionBootApp.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"--spring.cloud.stream.function.definition=toUpperCase"</span>);
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Function&lt;String, String&gt; toUpperCase() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> s -&gt; s.toUpperCase();
}
}</pre><p>In the above you we simply define a bean of type <code class="literal">java.util.function.Function</code> called <span class="emphasis"><em>toUpperCase</em></span> and identify it as a bean to be used as message handler
whose 'input' and 'output' must be bound to the external destinations exposed by the Processor binding.</p><p>Below are the examples of simple functional applications to support Source, Processor and Sink.</p><p>Here is the example of a Source application defined as <code class="literal">java.util.function.Supplier</code></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SourceFromSupplier {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(SourceFromSupplier.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"--spring.cloud.stream.function.definition=date"</span>);
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Supplier&lt;Date&gt; date() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> () -&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Date(<span class="hl-number">12345L</span>);
}
}</pre><p>Here is the example of a Processor application defined as <code class="literal">java.util.function.Function</code></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ProcessorFromFunction {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(ProcessorFromFunction.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"--spring.cloud.stream.function.definition=toUpperCase"</span>);
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Function&lt;String, String&gt; toUpperCase() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> s -&gt; s.toUpperCase();
}
}</pre><p>Here is the example of a Sink application defined as <code class="literal">java.util.function.Consumer</code></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SinkFromConsumer {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(SinkFromConsumer.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"--spring.cloud.stream.function.definition=sink"</span>);
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Consumer&lt;String&gt; sink() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> System.out::println;
}
}</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_functional_composition" href="#_functional_composition"></a>Functional Composition</h4></div></div></div><p>Using this programming model you can also benefit from functional composition where you can dynamically compose complex handlers from a set of simple functions.
As an example let&#8217;s add the following function bean to the application defined above</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Function&lt;String, String&gt; wrapInQuotes() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> s -&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\""</span> + s + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\""</span>;
}</pre><p>and modify the <code class="literal">spring.cloud.stream.function.definition</code> property to reflect your intention to compose a new function from both &#8216;toUpperCase&#8217; and &#8216;wrapInQuotes&#8217;.
To do that Spring Cloud Function allows you to use <code class="literal">|</code> (pipe) symbol. So to finish our example our property will now look like this:</p><pre class="programlisting">&#8212;spring.cloud.stream.function.definition=toUpperCase|wrapInQuotes</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-streams-overview-using-polled-consumers" href="#spring-cloud-streams-overview-using-polled-consumers"></a>28.3.5&nbsp;Using Polled Consumers</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_overview" href="#_overview"></a>Overview</h4></div></div></div><p>When using polled consumers, you poll the <code class="literal">PollableMessageSource</code> on demand.
Consider the following example of a polled consumer:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> PolledConsumer {
<em><span class="hl-annotation" style="color: gray">@Input</span></em>
PollableMessageSource destIn();
<em><span class="hl-annotation" style="color: gray">@Output</span></em>
MessageChannel destOut();
}</pre><p>Given the polled consumer in the preceding example, you might use it as follows:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ApplicationRunner poller(PollableMessageSource destIn, MessageChannel destOut) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> args -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">while</span> (someCondition()) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (!destIn.poll(m -&gt; {
String newPayload = ((String) m.getPayload()).toUpperCase();
destOut.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> GenericMessage&lt;&gt;(newPayload));
})) {
Thread.sleep(<span class="hl-number">1000</span>);
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (Exception e) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// handle failure</span>
}
}
};
}</pre><p>The <code class="literal">PollableMessageSource.poll()</code> method takes a <code class="literal">MessageHandler</code> argument (often a lambda expression, as shown here).
It returns <code class="literal">true</code> if the message was received and successfully processed.</p><p>As with message-driven consumers, if the <code class="literal">MessageHandler</code> throws an exception, messages are published to error channels, as discussed in &#8220;<a class="xref" href="#">???</a>&#8221;.</p><p>Normally, the <code class="literal">poll()</code> method acknowledges the message when the <code class="literal">MessageHandler</code> exits.
If the method exits abnormally, the message is rejected (not re-queued), but see <a class="xref" href="#polled-errors" title="Handling Errors">the section called &#8220;Handling Errors&#8221;</a>.
You can override that behavior by taking responsibility for the acknowledgment, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ApplicationRunner poller(PollableMessageSource dest1In, MessageChannel dest2Out) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> args -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">while</span> (someCondition()) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (!dest1In.poll(m -&gt; {
StaticMessageHeaderAccessor.getAcknowledgmentCallback(m).noAutoAck();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// e.g. hand off to another thread which can perform the ack</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// or acknowledge(Status.REQUEUE)</span>
})) {
Thread.sleep(<span class="hl-number">1000</span>);
}
}
};
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You must <code class="literal">ack</code> (or <code class="literal">nack</code>) the message at some point, to avoid resource leaks.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Some messaging systems (such as Apache Kafka) maintain a simple offset in a log. If a delivery fails and is re-queued with <code class="literal">StaticMessageHeaderAccessor.getAcknowledgmentCallback(m).acknowledge(Status.REQUEUE);</code>, any later successfully ack&#8217;d messages are redelivered.</p></td></tr></table></div><p>There is also an overloaded <code class="literal">poll</code> method, for which the definition is as follows:</p><pre class="programlisting">poll(MessageHandler handler, ParameterizedTypeReference&lt;?&gt; type)</pre><p>The <code class="literal">type</code> is a conversion hint that allows the incoming message payload to be converted, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> result = pollableSource.poll(received -&gt; {
Map&lt;String, Foo&gt; payload = (Map&lt;String, Foo&gt;) received.getPayload();
...
}, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ParameterizedTypeReference&lt;Map&lt;String, Foo&gt;&gt;() {});</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="polled-errors" href="#polled-errors"></a>Handling Errors</h4></div></div></div><p>By default, an error channel is configured for the pollable source; if the callback throws an exception, an <code class="literal">ErrorMessage</code> is sent to the error channel (<code class="literal">&lt;destination&gt;.&lt;group&gt;.errors</code>); this error channel is also bridged to the global Spring Integration <code class="literal">errorChannel</code>.</p><p>You can subscribe to either error channel with a <code class="literal">@ServiceActivator</code> to handle errors; without a subscription, the error will simply be logged and the message will be acknowledged as successful.
If the error channel service activator throws an exception, the message will be rejected (by default) and won&#8217;t be redelivered.
If the service activator throws a <code class="literal">RequeueCurrentMessageException</code>, the message will be requeued at the broker and will be again retrieved on a subsequent poll.</p><p>If the listener throws a <code class="literal">RequeueCurrentMessageException</code> directly, the message will be requeued, as discussed above, and will not be sent to the error channels.</p></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-error-handling" href="#spring-cloud-stream-overview-error-handling"></a>28.4&nbsp;Error Handling</h2></div></div></div><p>Errors happen, and Spring Cloud Stream provides several flexible mechanisms to handle them.
The error handling comes in two flavors:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>application:</strong></span> The error handling is done within the application (custom error handler).</li><li class="listitem"><span class="strong"><strong>system:</strong></span> The error handling is delegated to the binder (re-queue, DL, and others). Note that the techniques are dependent on binder implementation and the
capability of the underlying messaging middleware.</li></ul></div><p>Spring Cloud Stream uses the <a class="link" href="https://github.com/spring-projects/spring-retry" target="_top">Spring Retry</a> library to facilitate successful message processing. See <a class="xref" href="#_retry_template" title="28.4.3&nbsp;Retry Template">Section&nbsp;28.4.3, &#8220;Retry Template&#8221;</a> for more details.
However, when all fails, the exceptions thrown by the message handlers are propagated back to the binder. At that point, binder invokes custom error handler or communicates
the error back to the messaging system (re-queue, DLQ, and others).</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_application_error_handling" href="#_application_error_handling"></a>28.4.1&nbsp;Application Error Handling</h3></div></div></div><p>There are two types of application-level error handling. Errors can be handled at each binding subscription or a global handler can handle all the binding subscription errors. Let&#8217;s review the details.</p><div class="figure"><a name="d0e8554" href="#d0e8554"></a><p class="title"><b>Figure&nbsp;28.1.&nbsp;A Spring Cloud Stream Sink Application with Custom and Global Error Handlers</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/custom_vs_global_error_channels.png" align="middle" alt="custom vs global error channels"></div></div></div><br class="figure-break"><p>For each input binding, Spring Cloud Stream creates a dedicated error channel with the following semantics <code class="literal">&lt;destinationName&gt;.errors</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">&lt;destinationName&gt;</code> consists of the name of the binding (such as <code class="literal">input</code>) and the name of the group (such as <code class="literal">myGroup</code>).</p></td></tr></table></div><p>Consider the following:</p><pre class="programlisting">spring.cloud.stream.bindings.input.group=myGroup</pre><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// destination name 'input.myGroup'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handle(Person value) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> RuntimeException(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOM!"</span>);
}
<em><span class="hl-annotation" style="color: gray">@ServiceActivator(inputChannel = Processor.INPUT + ".myGroup.errors")</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//channel name 'input.myGroup.errors'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> error(Message&lt;?&gt; message) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Handling ERROR: "</span> + message);
}</pre><p>In the preceding example the destination name is <code class="literal">input.myGroup</code> and the dedicated error channel name is <code class="literal">input.myGroup.errors</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The use of @StreamListener annotation is intended specifically to define bindings that bridge internal channels and external destinations. Given that the destination
specific error channel does NOT have an associated external destination, such channel is a prerogative of Spring Integration (SI). This means that the handler
for such destination must be defined using one of the SI handler annotations (i.e., @ServiceActivator, @Transformer etc.).</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If <code class="literal">group</code> is not specified anonymous group is used (something like <code class="literal">input.anonymous.2K37rb06Q6m2r51-SPIDDQ</code>), which is not suitable for error
handling scenarious, since you don&#8217;t know what it&#8217;s going to be until the destination is created.</p></td></tr></table></div><p>Also, in the event you are binding to the existing destination such as:</p><pre class="programlisting">spring.cloud.stream.bindings.input.destination=myFooDestination
spring.cloud.stream.bindings.input.group=myGroup</pre><p>the full destination name is <code class="literal">myFooDestination.myGroup</code> and then the dedicated error channel name is <code class="literal">myFooDestination.myGroup.errors</code>.</p><p>Back to the example&#8230;&#8203;</p><p>The <code class="literal">handle(..)</code> method, which subscribes to the channel named <code class="literal">input</code>, throws an exception. Given there is also a subscriber to the error channel <code class="literal">input.myGroup.errors</code>
all error messages are handled by this subscriber.</p><p>If you have multiple bindings, you may want to have a single error handler. Spring Cloud Stream automatically provides support for
a <span class="emphasis"><em>global error channel</em></span> by bridging each individual error channel to the channel named <code class="literal">errorChannel</code>, allowing a single subscriber to handle all errors,
as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@StreamListener("errorChannel")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> error(Message&lt;?&gt; message) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Handling ERROR: "</span> + message);
}</pre><p>This may be a convenient option if error handling logic is the same regardless of which handler produced the error.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_system_error_handling" href="#_system_error_handling"></a>28.4.2&nbsp;System Error Handling</h3></div></div></div><p>System-level error handling implies that the errors are communicated back to the messaging system and, given that not every messaging system
is the same, the capabilities may differ from binder to binder.</p><p>That said, in this section we explain the general idea behind system level error handling and use Rabbit binder as an example. NOTE: Kafka binder provides similar
support, although some configuration properties do differ. Also, for more details and configuration options, see the individual binder&#8217;s documentation.</p><p>If no internal error handlers are configured, the errors propagate to the binders, and the binders subsequently propagate those errors back to the messaging system.
Depending on the capabilities of the messaging system such a system may <span class="emphasis"><em>drop</em></span> the message, <span class="emphasis"><em>re-queue</em></span> the message for re-processing or <span class="emphasis"><em>send the failed message to DLQ</em></span>.
Both Rabbit and Kafka support these concepts. However, other binders may not, so refer to your individual binder&#8217;s documentation for details on supported system-level
error-handling options.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_drop_failed_messages" href="#_drop_failed_messages"></a>Drop Failed Messages</h4></div></div></div><p>By default, if no additional system-level configuration is provided, the messaging system drops the failed message.
While acceptable in some cases, for most cases, it is not, and we need some recovery mechanism to avoid message loss.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_dlq_dead_letter_queue" href="#_dlq_dead_letter_queue"></a>DLQ - Dead Letter Queue</h4></div></div></div><p>DLQ allows failed messages to be sent to a special destination: - <span class="emphasis"><em>Dead Letter Queue</em></span>.</p><p>When configured, failed messages are sent to this destination for subsequent re-processing or auditing and reconciliation.</p><p>For example, continuing on the previous example and to set up the DLQ with Rabbit binder, you need to set the following property:</p><pre class="programlisting">spring.cloud.stream.rabbit.bindings.input.consumer.auto-bind-dlq=true</pre><p>Keep in mind that, in the above property, <code class="literal">input</code> corresponds to the name of the input destination binding.
The <code class="literal">consumer</code> indicates that it is a consumer property and <code class="literal">auto-bind-dlq</code> instructs the binder to configure DLQ for <code class="literal">input</code>
destination, which results in an additional Rabbit queue named <code class="literal">input.myGroup.dlq</code>.</p><p>Once configured, all failed messages are routed to this queue with an error message similar to the following:</p><pre class="programlisting">delivery_mode: 1
headers:
x-death:
count: 1
reason: rejected
queue: input.hello
time: 1522328151
exchange:
routing-keys: input.myGroup
Payload {"name&#8221;:"Bob"}</pre><p>As you can see from the above, your original message is preserved for further actions.</p><p>However, one thing you may have noticed is that there is limited information on the original issue with the message processing. For example, you do not see a stack
trace corresponding to the original error.
To get more relevant information about the original error, you must set an additional property:</p><pre class="programlisting">spring.cloud.stream.rabbit.bindings.input.consumer.republish-to-dlq=true</pre><p>Doing so forces the internal error handler to intercept the error message and add additional information to it before publishing it to DLQ.
Once configured, you can see that the error message contains more information relevant to the original error, as follows:</p><pre class="programlisting">delivery_mode: 2
headers:
x-original-exchange:
x-exception-message: has an error
x-original-routingKey: input.myGroup
x-exception-stacktrace: org.springframework.messaging.MessageHandlingException: nested exception is
org.springframework.messaging.MessagingException: has an error, failedMessage=GenericMessage [payload=byte[15],
headers={amqp_receivedDeliveryMode=NON_PERSISTENT, amqp_receivedRoutingKey=input.hello, amqp_deliveryTag=1,
deliveryAttempt=3, amqp_consumerQueue=input.hello, amqp_redelivered=false, id=a15231e6-3f80-677b-5ad7-d4b1e61e486e,
amqp_consumerTag=amq.ctag-skBFapilvtZhDsn0k3ZmQg, contentType=application/json, timestamp=1522327846136}]
at org.spring...integ...han...MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:107)
at. . . . .
Payload {"name&#8221;:"Bob"}</pre><p>This effectively combines application-level and system-level error handling to further assist with downstream troubleshooting mechanics.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_re_queue_failed_messages" href="#_re_queue_failed_messages"></a>Re-queue Failed Messages</h4></div></div></div><p>As mentioned earlier, the currently supported binders (Rabbit and Kafka) rely on <code class="literal">RetryTemplate</code> to facilitate successful message processing. See <a class="xref" href="#_retry_template" title="28.4.3&nbsp;Retry Template">Section&nbsp;28.4.3, &#8220;Retry Template&#8221;</a> for details.
However, for cases when <code class="literal">max-attempts</code> property is set to 1, internal reprocessing of the message is disabled. At this point, you can facilitate message re-processing (re-tries)
by instructing the messaging system to re-queue the failed message. Once re-queued, the failed message is sent back to the original handler, essentially creating a retry loop.</p><p>This option may be feasible for cases where the nature of the error is related to some sporadic yet short-term unavailability of some resource.</p><p>To accomplish that, you must set the following properties:</p><pre class="programlisting">spring.cloud.stream.bindings.input.consumer.max-attempts=1
spring.cloud.stream.rabbit.bindings.input.consumer.requeue-rejected=true</pre><p>In the preceding example, the <code class="literal">max-attempts</code> set to 1 essentially disabling internal re-tries and <code class="literal">requeue-rejected</code> (short for <span class="emphasis"><em>requeue rejected messages</em></span>) is set to <code class="literal">true</code>.
Once set, the failed message is resubmitted to the same handler and loops continuously or until the handler throws <code class="literal">AmqpRejectAndDontRequeueException</code>
essentially allowing you to build your own re-try logic within the handler itself.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_retry_template" href="#_retry_template"></a>28.4.3&nbsp;Retry Template</h3></div></div></div><p>The <code class="literal">RetryTemplate</code> is part of the <a class="link" href="https://github.com/spring-projects/spring-retry" target="_top">Spring Retry</a> library.
While it is out of scope of this document to cover all of the capabilities of the <code class="literal">RetryTemplate</code>, we will mention the following consumer properties that are specifically related to
the <code class="literal">RetryTemplate</code>:</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">maxAttempts</span></dt><dd><p class="simpara">The number of attempts to process the message.</p><p class="simpara">Default: 3.</p></dd><dt><span class="term">backOffInitialInterval</span></dt><dd><p class="simpara">The backoff initial interval on retry.</p><p class="simpara">Default 1000 milliseconds.</p></dd><dt><span class="term">backOffMaxInterval</span></dt><dd><p class="simpara">The maximum backoff interval.</p><p class="simpara">Default 10000 milliseconds.</p></dd><dt><span class="term">backOffMultiplier</span></dt><dd><p class="simpara">The backoff multiplier.</p><p class="simpara">Default 2.0.</p></dd><dt><span class="term">defaultRetryable</span></dt><dd><p class="simpara">Whether exceptions thrown by the listener that are not listed in the <code class="literal">retryableExceptions</code> are retryable.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">retryableExceptions</span></dt><dd><p class="simpara">A map of Throwable class names in the key and a boolean in the value.
Specify those exceptions (and subclasses) that will or won&#8217;t be retried.
Also see <code class="literal">defaultRetriable</code>.
Example: <code class="literal">spring.cloud.stream.bindings.input.consumer.retryable-exceptions.java.lang.IllegalStateException=false</code>.</p><p class="simpara">Default: empty.</p></dd></dl></div><p>While the preceding settings are sufficient for majority of the customization requirements, they may not satisfy certain complex requirements at, which
point you may want to provide your own instance of the <code class="literal">RetryTemplate</code>. To do so configure it as a bean in your application configuration. The application provided
instance will override the one provided by the framework. Also, to avoid conflicts you must qualify the instance of the <code class="literal">RetryTemplate</code> you want to be used by the binder
as <code class="literal">@StreamRetryTemplate</code>. For example,</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@StreamRetryTemplate</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> RetryTemplate myRetryTemplate() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> RetryTemplate();
}</pre><p>As you can see from the above example you don&#8217;t need to annotate it with <code class="literal">@Bean</code> since <code class="literal">@StreamRetryTemplate</code> is a qualified <code class="literal">@Bean</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-reactive-programming-support" href="#spring-cloud-stream-overview-reactive-programming-support"></a>28.5&nbsp;Reactive Programming Support</h2></div></div></div><p>Spring Cloud Stream also supports the use of reactive APIs where incoming and outgoing data is handled as continuous data flows.
Support for reactive APIs is available through <code class="literal">spring-cloud-stream-reactive</code>, which needs to be added explicitly to your project.</p><p>The programming model with reactive APIs is declarative. Instead of specifying how each individual message should be handled, you can use operators that describe functional transformations from inbound to outbound data flows.</p><p>At present Spring Cloud Stream supports the only the <a class="link" href="https://projectreactor.io/" target="_top">Reactor API</a>.
In the future, we intend to support a more generic model based on Reactive Streams.</p><p>The reactive programming model also uses the <code class="literal">@StreamListener</code> annotation for setting up reactive handlers.
The differences are that:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The <code class="literal">@StreamListener</code> annotation must not specify an input or output, as they are provided as arguments and return values from the method.</li><li class="listitem">The arguments of the method must be annotated with <code class="literal">@Input</code> and <code class="literal">@Output</code>, indicating which input or output the incoming and outgoing data flows connect to, respectively.</li><li class="listitem">The return value of the method, if any, is annotated with <code class="literal">@Output</code>, indicating the input where data should be sent.</li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Reactive programming support requires Java 1.8.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>As of Spring Cloud Stream 1.1.1 and later (starting with release train Brooklyn.SR2), reactive programming support requires the use of Reactor 3.0.4.RELEASE and higher.
Earlier Reactor versions (including 3.0.1.RELEASE, 3.0.2.RELEASE and 3.0.3.RELEASE) are not supported.
<code class="literal">spring-cloud-stream-reactive</code> transitively retrieves the proper version, but it is possible for the project structure to manage the version of the <code class="literal">io.projectreactor:reactor-core</code> to an earlier release, especially when using Maven.
This is the case for projects generated by using Spring Initializr with Spring Boot 1.x, which overrides the Reactor version to <code class="literal">2.0.8.RELEASE</code>.
In such cases, you must ensure that the proper version of the artifact is released.
You can do so by adding a direct dependency on <code class="literal">io.projectreactor:reactor-core</code> with a version of <code class="literal">3.0.4.RELEASE</code> or later to your project.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The use of term, &#8220;reactive&#8221;, currently refers to the reactive APIs being used and not to the execution model being reactive (that is, the bound endpoints still use a 'push' rather than a 'pull' model). While some backpressure support is provided by the use of Reactor, we do intend, in a future release, to support entirely reactive pipelines by the use of native reactive clients for the connected middleware.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_reactor_based_handlers" href="#_reactor_based_handlers"></a>28.5.1&nbsp;Reactor-based Handlers</h3></div></div></div><p>A Reactor-based handler can have the following argument types:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">For arguments annotated with <code class="literal">@Input</code>, it supports the Reactor <code class="literal">Flux</code> type.
The parameterization of the inbound Flux follows the same rules as in the case of individual message handling: It can be the entire <code class="literal">Message</code>, a POJO that can be the <code class="literal">Message</code> payload, or a POJO that is the result of a transformation based on the <code class="literal">Message</code> content-type header. Multiple inputs are provided.</li><li class="listitem">For arguments annotated with <code class="literal">Output</code>, it supports the <code class="literal">FluxSender</code> type, which connects a <code class="literal">Flux</code> produced by the method with an output. Generally speaking, specifying outputs as arguments is only recommended when the method can have multiple outputs.</li></ul></div><p>A Reactor-based handler supports a return type of <code class="literal">Flux</code>. In that case, it must be annotated with <code class="literal">@Output</code>. We recommend using the return value of the method when a single output <code class="literal">Flux</code> is available.</p><p>The following example shows a Reactor-based <code class="literal">Processor</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> UppercaseTransformer {
<em><span class="hl-annotation" style="color: gray">@StreamListener</span></em>
<em><span class="hl-annotation" style="color: gray">@Output(Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Flux&lt;String&gt; receive(<em><span class="hl-annotation" style="color: gray">@Input(Processor.INPUT)</span></em> Flux&lt;String&gt; input) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> input.map(s -&gt; s.toUpperCase());
}
}</pre><p>The same processor using output arguments looks like the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> UppercaseTransformer {
<em><span class="hl-annotation" style="color: gray">@StreamListener</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> receive(<em><span class="hl-annotation" style="color: gray">@Input(Processor.INPUT)</span></em> Flux&lt;String&gt; input,
<em><span class="hl-annotation" style="color: gray">@Output(Processor.OUTPUT)</span></em> FluxSender output) {
output.send(input.map(s -&gt; s.toUpperCase()));
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_reactive_sources" href="#_reactive_sources"></a>28.5.2&nbsp;Reactive Sources</h3></div></div></div><p>Spring Cloud Stream reactive support also provides the ability for creating reactive sources through the <code class="literal">@StreamEmitter</code> annotation.
By using the <code class="literal">@StreamEmitter</code> annotation, a regular source may be converted to a reactive one.
<code class="literal">@StreamEmitter</code> is a method level annotation that marks a method to be an emitter to outputs declared with <code class="literal">@EnableBinding</code>.
You cannot use the <code class="literal">@Input</code> annotation along with <code class="literal">@StreamEmitter</code>, as the methods marked with this annotation are not listening for any input. Rather, methods marked with <code class="literal">@StreamEmitter</code> generate output.
Following the same programming model used in <code class="literal">@StreamListener</code>, <code class="literal">@StreamEmitter</code> also allows flexible ways of using the <code class="literal">@Output</code> annotation, depending on whether the method has any arguments, a return type, and other considerations.</p><p>The remainder of this section contains examples of using the <code class="literal">@StreamEmitter</code> annotation in various styles.</p><p>The following example emits the <code class="literal">Hello, World</code> message every millisecond and publishes to a Reactor <code class="literal">Flux</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> HelloWorldEmitter {
<em><span class="hl-annotation" style="color: gray">@StreamEmitter</span></em>
<em><span class="hl-annotation" style="color: gray">@Output(Source.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Flux&lt;String&gt; emit() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Flux.intervalMillis(<span class="hl-number">1</span>)
.map(l -&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>);
}
}</pre><p>In the preceding example, the resulting messages in the <code class="literal">Flux</code> are sent to the output channel of the <code class="literal">Source</code>.</p><p>The next example is another flavor of an <code class="literal">@StreamEmmitter</code> that sends a Reactor <code class="literal">Flux</code>.
Instead of returning a <code class="literal">Flux</code>, the following method uses a <code class="literal">FluxSender</code> to programmatically send a <code class="literal">Flux</code> from a source:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> HelloWorldEmitter {
<em><span class="hl-annotation" style="color: gray">@StreamEmitter</span></em>
<em><span class="hl-annotation" style="color: gray">@Output(Source.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> emit(FluxSender output) {
output.send(Flux.intervalMillis(<span class="hl-number">1</span>)
.map(l -&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>));
}
}</pre><p>The next example is exactly same as the above snippet in functionality and style.
However, instead of using an explicit <code class="literal">@Output</code> annotation on the method, it uses the annotation on the method parameter.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> HelloWorldEmitter {
<em><span class="hl-annotation" style="color: gray">@StreamEmitter</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> emit(<em><span class="hl-annotation" style="color: gray">@Output(Source.OUTPUT)</span></em> FluxSender output) {
output.send(Flux.intervalMillis(<span class="hl-number">1</span>)
.map(l -&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>));
}
}</pre><p>The last example in this section is yet another flavor of writing reacting sources by using the Reactive Streams Publisher API and taking advantage of the support for it in <a class="link" href="https://github.com/spring-projects/spring-integration-java-dsl/wiki/Spring-Integration-Java-DSL-Reference" target="_top">Spring Integration Java DSL</a>.
The <code class="literal">Publisher</code> in the following example still uses Reactor <code class="literal">Flux</code> under the hood, but, from an application perspective, that is transparent to the user and only needs Reactive Streams and Java DSL for Spring Integration:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> HelloWorldEmitter {
<em><span class="hl-annotation" style="color: gray">@StreamEmitter</span></em>
<em><span class="hl-annotation" style="color: gray">@Output(Source.OUTPUT)</span></em>
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Publisher&lt;Message&lt;String&gt;&gt; emit() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> IntegrationFlows.from(() -&gt;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> GenericMessage&lt;&gt;(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>),
e -&gt; e.poller(p -&gt; p.fixedDelay(<span class="hl-number">1</span>)))
.toReactivePublisher();
}
}</pre></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-stream-overview-binders" href="#spring-cloud-stream-overview-binders"></a>29.&nbsp;Binders</h2></div></div></div><p>Spring Cloud Stream provides a Binder abstraction for use in connecting to physical destinations at the external middleware.
This section provides information about the main concepts behind the Binder SPI, its main components, and implementation-specific details.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_producers_and_consumers" href="#_producers_and_consumers"></a>29.1&nbsp;Producers and Consumers</h2></div></div></div><p>The following image shows the general relationship of producers and consumers:</p><div class="figure"><a name="d0e9082" href="#d0e9082"></a><p class="title"><b>Figure&nbsp;29.1.&nbsp;Producers and Consumers</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/producers-consumers.png" align="middle" alt="producers consumers"></div></div></div><br class="figure-break"><p>A producer is any component that sends messages to a channel.
The channel can be bound to an external message broker with a <code class="literal">Binder</code> implementation for that broker.
When invoking the <code class="literal">bindProducer()</code> method, the first parameter is the name of the destination within the broker, the second parameter is the local channel instance to which the producer sends messages, and the third parameter contains properties (such as a partition key expression) to be used within the adapter that is created for that channel.</p><p>A consumer is any component that receives messages from a channel.
As with a producer, the consumer&#8217;s channel can be bound to an external message broker.
When invoking the <code class="literal">bindConsumer()</code> method, the first parameter is the destination name, and a second parameter provides the name of a logical group of consumers.
Each group that is represented by consumer bindings for a given destination receives a copy of each message that a producer sends to that destination (that is, it follows normal publish-subscribe semantics).
If there are multiple consumer instances bound with the same group name, then messages are load-balanced across those consumer instances so that each message sent by a producer is consumed by only a single consumer instance within each group (that is, it follows normal queueing semantics).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-binder-api" href="#spring-cloud-stream-overview-binder-api"></a>29.2&nbsp;Binder SPI</h2></div></div></div><p>The Binder SPI consists of a number of interfaces, out-of-the box utility classes, and discovery strategies that provide a pluggable mechanism for connecting to external middleware.</p><p>The key point of the SPI is the <code class="literal">Binder</code> interface, which is a strategy for connecting inputs and outputs to external middleware. The following listing shows the definnition of the <code class="literal">Binder</code> interface:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> Binder&lt;T, C <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> ConsumerProperties, P <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> ProducerProperties&gt; {
Binding&lt;T&gt; bindConsumer(String name, String group, T inboundBindTarget, C consumerProperties);
Binding&lt;T&gt; bindProducer(String name, T outboundBindTarget, P producerProperties);
}</pre><p>The interface is parameterized, offering a number of extension points:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Input and output bind targets. As of version 1.0, only <code class="literal">MessageChannel</code> is supported, but this is intended to be used as an extension point in the future.</li><li class="listitem">Extended consumer and producer properties, allowing specific Binder implementations to add supplemental properties that can be supported in a type-safe manner.</li></ul></div><p>A typical binder implementation consists of the following:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">A class that implements the <code class="literal">Binder</code> interface;</li><li class="listitem">A Spring <code class="literal">@Configuration</code> class that creates a bean of type <code class="literal">Binder</code> along with the middleware connection infrastructure.</li><li class="listitem"><p class="simpara">A <code class="literal">META-INF/spring.binders</code> file found on the classpath containing one or more binder definitions, as shown in the following example:</p><pre class="screen">kafka:\
org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfiguration</pre></li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_binder_detection" href="#_binder_detection"></a>29.3&nbsp;Binder Detection</h2></div></div></div><p>Spring Cloud Stream relies on implementations of the Binder SPI to perform the task of connecting channels to message brokers.
Each Binder implementation typically connects to one type of messaging system.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_classpath_detection" href="#_classpath_detection"></a>29.3.1&nbsp;Classpath Detection</h3></div></div></div><p>By default, Spring Cloud Stream relies on Spring Boot&#8217;s auto-configuration to configure the binding process.
If a single Binder implementation is found on the classpath, Spring Cloud Stream automatically uses it.
For example, a Spring Cloud Stream project that aims to bind only to RabbitMQ can add the following dependency:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-binder-rabbit<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>For the specific Maven coordinates of other binder dependencies, see the documentation of that binder implementation.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="multiple-binders" href="#multiple-binders"></a>29.4&nbsp;Multiple Binders on the Classpath</h2></div></div></div><p>When multiple binders are present on the classpath, the application must indicate which binder is to be used for each channel binding.
Each binder configuration contains a <code class="literal">META-INF/spring.binders</code> file, which is a simple properties file, as shown in the following example:</p><pre class="screen">rabbit:\
org.springframework.cloud.stream.binder.rabbit.config.RabbitServiceAutoConfiguration</pre><p>Similar files exist for the other provided binder implementations (such as Kafka), and custom binder implementations are expected to provide them as well.
The key represents an identifying name for the binder implementation, whereas the value is a comma-separated list of configuration classes that each contain one and only one bean definition of type <code class="literal">org.springframework.cloud.stream.binder.Binder</code>.</p><p>Binder selection can either be performed globally, using the <code class="literal">spring.cloud.stream.defaultBinder</code> property (for example, <code class="literal">spring.cloud.stream.defaultBinder=rabbit</code>) or individually, by configuring the binder on each channel binding.
For instance, a processor application (that has channels named <code class="literal">input</code> and <code class="literal">output</code> for read and write respectively) that reads from Kafka and writes to RabbitMQ can specify the following configuration:</p><pre class="screen">spring.cloud.stream.bindings.input.binder=kafka
spring.cloud.stream.bindings.output.binder=rabbit</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="multiple-systems" href="#multiple-systems"></a>29.5&nbsp;Connecting to Multiple Systems</h2></div></div></div><p>By default, binders share the application&#8217;s Spring Boot auto-configuration, so that one instance of each binder found on the classpath is created.
If your application should connect to more than one broker of the same type, you can specify multiple binder configurations, each with different environment settings.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Turning on explicit binder configuration disables the default binder configuration process altogether.
If you do so, all binders in use must be included in the configuration.
Frameworks that intend to use Spring Cloud Stream transparently may create binder configurations that can be referenced by name, but they do not affect the default binder configuration.
In order to do so, a binder configuration may have its <code class="literal">defaultCandidate</code> flag set to false (for example, <code class="literal">spring.cloud.stream.binders.&lt;configurationName&gt;.defaultCandidate=false</code>).
This denotes a configuration that exists independently of the default binder configuration process.</p></td></tr></table></div><p>The following example shows a typical configuration for a processor application that connects to two RabbitMQ broker instances:</p><pre class="programlisting">spring:
cloud:
stream:
bindings:
input:
destination: thing1
binder: rabbit1
output:
destination: thing2
binder: rabbit2
binders:
rabbit1:
type: rabbit
environment:
spring:
rabbitmq:
host: &lt;host1&gt;
rabbit2:
type: rabbit
environment:
spring:
rabbitmq:
host: &lt;host2&gt;</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_binding_visualization_and_control" href="#_binding_visualization_and_control"></a>29.6&nbsp;Binding visualization and control</h2></div></div></div><p>Since version 2.0, Spring Cloud Stream supports visualization and control of the Bindings through Actuator endpoints.</p><p>Starting with version 2.0 actuator and web are optional, you must first add one of the web dependencies as well as add the actuator dependency manually.
The following example shows how to add the dependency for the Web framework:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-web<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>The following example shows how to add the dependency for the WebFlux framework:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-webflux<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>You can add the Actuator dependency as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-actuator<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>To run Spring Cloud Stream 2.0 apps in Cloud Foundry, you must add <code class="literal">spring-boot-starter-web</code> and <code class="literal">spring-boot-starter-actuator</code> to the classpath. Otherwise, the
application will not start due to health check failures.</p></td></tr></table></div><p>You must also enable the <code class="literal">bindings</code> actuator endpoints by setting the following property: <code class="literal">--management.endpoints.web.exposure.include=bindings</code>.</p><p>Once those prerequisites are satisfied. you should see the following in the logs when application start:</p><pre class="literallayout">: Mapped "{[/actuator/bindings/{name}],methods=[POST]. . .
: Mapped "{[/actuator/bindings],methods=[GET]. . .
: Mapped "{[/actuator/bindings/{name}],methods=[GET]. . .</pre><p>To visualize the current bindings, access the following URL:
<code class="literal"><a class="link" href="http://<host&gt;:<port&gt;/actuator/bindings" target="_top">http://&lt;host&gt;:&lt;port&gt;/actuator/bindings</a></code></p><p>Alternative, to see a single binding, access one of the URLs similar to the following:
<code class="literal"><a class="link" href="http://<host&gt;:<port&gt;/actuator/bindings/myBindingName" target="_top">http://&lt;host&gt;:&lt;port&gt;/actuator/bindings/myBindingName</a></code></p><p>You can also stop, start, pause, and resume individual bindings by posting to the same URL while providing a <code class="literal">state</code> argument as JSON, as shown in the following examples:</p><p>curl -d '{"state":"STOPPED"}' -H "Content-Type: application/json" -X POST <a class="link" href="http://<host&gt;:<port&gt;/actuator/bindings/myBindingName" target="_top">http://&lt;host&gt;:&lt;port&gt;/actuator/bindings/myBindingName</a>
curl -d '{"state":"STARTED"}' -H "Content-Type: application/json" -X POST <a class="link" href="http://<host&gt;:<port&gt;/actuator/bindings/myBindingName" target="_top">http://&lt;host&gt;:&lt;port&gt;/actuator/bindings/myBindingName</a>
curl -d '{"state":"PAUSED"}' -H "Content-Type: application/json" -X POST <a class="link" href="http://<host&gt;:<port&gt;/actuator/bindings/myBindingName" target="_top">http://&lt;host&gt;:&lt;port&gt;/actuator/bindings/myBindingName</a>
curl -d '{"state":"RESUMED"}' -H "Content-Type: application/json" -X POST <a class="link" href="http://<host&gt;:<port&gt;/actuator/bindings/myBindingName" target="_top">http://&lt;host&gt;:&lt;port&gt;/actuator/bindings/myBindingName</a></p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><code class="literal">PAUSED</code> and <code class="literal">RESUMED</code> work only when the corresponding binder and its underlying technology supports it. Otherwise, you see the warning message in the logs.
Currently, only Kafka binder supports the <code class="literal">PAUSED</code> and <code class="literal">RESUMED</code> states.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_binder_configuration_properties" href="#_binder_configuration_properties"></a>29.7&nbsp;Binder Configuration Properties</h2></div></div></div><p>The following properties are available when customizing binder configurations. These properties exposed via <code class="literal">org.springframework.cloud.stream.config.BinderProperties</code></p><p>They must be prefixed with <code class="literal">spring.cloud.stream.binders.&lt;configurationName&gt;</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">type</span></dt><dd><p class="simpara">The binder type.
It typically references one of the binders found on the classpath&#8201;&#8212;&#8201;in particular, a key in a <code class="literal">META-INF/spring.binders</code> file.</p><p class="simpara">By default, it has the same value as the configuration name.</p></dd><dt><span class="term">inheritEnvironment</span></dt><dd><p class="simpara">Whether the configuration inherits the environment of the application itself.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">environment</span></dt><dd><p class="simpara">Root for a set of properties that can be used to customize the environment of the binder.
When this property is set, the context in which the binder is being created is not a child of the application context.
This setting allows for complete separation between the binder components and the application components.</p><p class="simpara">Default: <code class="literal">empty</code>.</p></dd><dt><span class="term">defaultCandidate</span></dt><dd><p class="simpara">Whether the binder configuration is a candidate for being considered a default binder or can be used only when explicitly referenced.
This setting allows adding binder configurations without interfering with the default processing.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd></dl></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_configuration_options" href="#_configuration_options"></a>30.&nbsp;Configuration Options</h2></div></div></div><p>Spring Cloud Stream supports general configuration options as well as configuration for bindings and binders.
Some binders let additional binding properties support middleware-specific features.</p><p>Configuration options can be provided to Spring Cloud Stream applications through any mechanism supported by Spring Boot.
This includes application arguments, environment variables, and YAML or .properties files.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_binding_service_properties" href="#_binding_service_properties"></a>30.1&nbsp;Binding Service Properties</h2></div></div></div><p>These properties are exposed via <code class="literal">org.springframework.cloud.stream.config.BindingServiceProperties</code></p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.instanceCount</span></dt><dd><p class="simpara">The number of deployed instances of an application.
Must be set for partitioning on the producer side. Must be set on the consumer side when using RabbitMQ and with Kafka if <code class="literal">autoRebalanceEnabled=false</code>.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">spring.cloud.stream.instanceIndex</span></dt><dd>The instance index of the application: A number from <code class="literal">0</code> to <code class="literal">instanceCount - 1</code>.
Used for partitioning with RabbitMQ and with Kafka if <code class="literal">autoRebalanceEnabled=false</code>.
Automatically set in Cloud Foundry to match the application&#8217;s instance index.</dd><dt><span class="term">spring.cloud.stream.dynamicDestinations</span></dt><dd><p class="simpara">A list of destinations that can be bound dynamically (for example, in a dynamic routing scenario).
If set, only listed destinations can be bound.</p><p class="simpara">Default: empty (letting any destination be bound).</p></dd><dt><span class="term">spring.cloud.stream.defaultBinder</span></dt><dd><p class="simpara">The default binder to use, if multiple binders are configured.
See <a class="link" href="#multiple-binders" title="29.4&nbsp;Multiple Binders on the Classpath">Multiple Binders on the Classpath</a>.</p><p class="simpara">Default: empty.</p></dd><dt><span class="term">spring.cloud.stream.overrideCloudConnectors</span></dt><dd><p class="simpara">This property is only applicable when the <code class="literal">cloud</code> profile is active and Spring Cloud Connectors are provided with the application.
If the property is <code class="literal">false</code> (the default), the binder detects a suitable bound service (for example, a RabbitMQ service bound in Cloud Foundry for the RabbitMQ binder) and uses it for creating connections (usually through Spring Cloud Connectors).
When set to <code class="literal">true</code>, this property instructs binders to completely ignore the bound services and rely on Spring Boot properties (for example, relying on the <code class="literal">spring.rabbitmq.*</code> properties provided in the environment for the RabbitMQ binder).
The typical usage of this property is to be nested in a customized environment <a class="link" href="#multiple-systems" title="29.5&nbsp;Connecting to Multiple Systems">when connecting to multiple systems</a>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">spring.cloud.stream.bindingRetryInterval</span></dt><dd><p class="simpara">The interval (in seconds) between retrying binding creation when, for example, the binder does not support late binding and the broker (for example, Apache Kafka) is down.
Set it to zero to treat such conditions as fatal, preventing the application from starting.</p><p class="simpara">Default: <code class="literal">30</code></p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="binding-properties" href="#binding-properties"></a>30.2&nbsp;Binding Properties</h2></div></div></div><p>Binding properties are supplied by using the format of <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.&lt;property&gt;=&lt;value&gt;</code>.
The <code class="literal">&lt;channelName&gt;</code> represents the name of the channel being configured (for example, <code class="literal">output</code> for a <code class="literal">Source</code>).</p><p>To avoid repetition, Spring Cloud Stream supports setting values for all channels, in the format of <code class="literal">spring.cloud.stream.default.&lt;property&gt;=&lt;value&gt;</code>.</p><p>When it comes to avoiding repetitions for extended binding properties, this format should be used - <code class="literal">spring.cloud.stream.&lt;binder-type&gt;.default.&lt;producer|consumer&gt;.&lt;property&gt;=&lt;value&gt;</code>.</p><p>In what follows, we indicate where we have omitted the <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.</code> prefix and focus just on the property name, with the understanding that the prefix ise included at runtime.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_common_binding_properties" href="#_common_binding_properties"></a>30.2.1&nbsp;Common Binding Properties</h3></div></div></div><p>These properties are exposed via <code class="literal">org.springframework.cloud.stream.config.BindingProperties</code></p><p>The following binding properties are available for both input and output bindings and must be prefixed with <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.</code> (for example, <code class="literal">spring.cloud.stream.bindings.input.destination=ticktock</code>).</p><p>Default values can be set by using the <code class="literal">spring.cloud.stream.default</code> prefix (for example`spring.cloud.stream.default.contentType=application/json`).</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">destination</span></dt><dd>The target destination of a channel on the bound middleware (for example, the RabbitMQ exchange or Kafka topic).
If the channel is bound as a consumer, it could be bound to multiple destinations, and the destination names can be specified as comma-separated <code class="literal">String</code> values.
If not set, the channel name is used instead.
The default value of this property cannot be overridden.</dd><dt><span class="term">group</span></dt><dd><p class="simpara">The consumer group of the channel.
Applies only to inbound bindings.
See <a class="link" href="#consumer-groups" title="27.4&nbsp;Consumer Groups">Consumer Groups</a>.</p><p class="simpara">Default: <code class="literal">null</code> (indicating an anonymous consumer).</p></dd><dt><span class="term">contentType</span></dt><dd><p class="simpara">The content type of the channel.
See &#8220;<a class="xref" href="#content-type-management" title="31.&nbsp;Content Type Negotiation">Chapter&nbsp;31, <i>Content Type Negotiation</i></a>&#8221;.</p><p class="simpara">Default: <code class="literal">application/json</code>.</p></dd><dt><span class="term">binder</span></dt><dd><p class="simpara">The binder used by this binding.
See &#8220;<a class="xref" href="#multiple-binders" title="29.4&nbsp;Multiple Binders on the Classpath">Section&nbsp;29.4, &#8220;Multiple Binders on the Classpath&#8221;</a>&#8221; for details.</p><p class="simpara">Default: <code class="literal">null</code> (the default binder is used, if it exists).</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_consumer_properties" href="#_consumer_properties"></a>30.2.2&nbsp;Consumer Properties</h3></div></div></div><p>These properties are exposed via <code class="literal">org.springframework.cloud.stream.binder.ConsumerProperties</code></p><p>The following binding properties are available for input bindings only and must be prefixed with <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.consumer.</code> (for example, <code class="literal">spring.cloud.stream.bindings.input.consumer.concurrency=3</code>).</p><p>Default values can be set by using the <code class="literal">spring.cloud.stream.default.consumer</code> prefix (for example, <code class="literal">spring.cloud.stream.default.consumer.headerMode=none</code>).</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">concurrency</span></dt><dd><p class="simpara">The concurrency of the inbound consumer.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">partitioned</span></dt><dd><p class="simpara">Whether the consumer receives data from a partitioned producer.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">headerMode</span></dt><dd><p class="simpara">When set to <code class="literal">none</code>, disables header parsing on input.
Effective only for messaging middleware that does not support message headers natively and requires header embedding.
This option is useful when consuming data from non-Spring Cloud Stream applications when native headers are not supported.
When set to <code class="literal">headers</code>, it uses the middleware&#8217;s native header mechanism.
When set to <code class="literal">embeddedHeaders</code>, it embeds headers into the message payload.</p><p class="simpara">Default: depends on the binder implementation.</p></dd><dt><span class="term">maxAttempts</span></dt><dd><p class="simpara">If processing fails, the number of attempts to process the message (including the first).
Set to <code class="literal">1</code> to disable retry.</p><p class="simpara">Default: <code class="literal">3</code>.</p></dd><dt><span class="term">backOffInitialInterval</span></dt><dd><p class="simpara">The backoff initial interval on retry.</p><p class="simpara">Default: <code class="literal">1000</code>.</p></dd><dt><span class="term">backOffMaxInterval</span></dt><dd><p class="simpara">The maximum backoff interval.</p><p class="simpara">Default: <code class="literal">10000</code>.</p></dd><dt><span class="term">backOffMultiplier</span></dt><dd><p class="simpara">The backoff multiplier.</p><p class="simpara">Default: <code class="literal">2.0</code>.</p></dd><dt><span class="term">defaultRetryable</span></dt><dd><p class="simpara">Whether exceptions thrown by the listener that are not listed in the <code class="literal">retryableExceptions</code> are retryable.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">instanceIndex</span></dt><dd><p class="simpara">When set to a value greater than equal to zero, it allows customizing the instance index of this consumer (if different from <code class="literal">spring.cloud.stream.instanceIndex</code>).
When set to a negative value, it defaults to <code class="literal">spring.cloud.stream.instanceIndex</code>.
See &#8220;<a class="xref" href="#spring-cloud-stream-overview-instance-index-instance-count" title="33.2&nbsp;Instance Index and Instance Count">Section&nbsp;33.2, &#8220;Instance Index and Instance Count&#8221;</a>&#8221; for more information.</p><p class="simpara">Default: <code class="literal">-1</code>.</p></dd><dt><span class="term">instanceCount</span></dt><dd><p class="simpara">When set to a value greater than equal to zero, it allows customizing the instance count of this consumer (if different from <code class="literal">spring.cloud.stream.instanceCount</code>).
When set to a negative value, it defaults to <code class="literal">spring.cloud.stream.instanceCount</code>.
See &#8220;<a class="xref" href="#spring-cloud-stream-overview-instance-index-instance-count" title="33.2&nbsp;Instance Index and Instance Count">Section&nbsp;33.2, &#8220;Instance Index and Instance Count&#8221;</a>&#8221; for more information.</p><p class="simpara">Default: <code class="literal">-1</code>.</p></dd><dt><span class="term">retryableExceptions</span></dt><dd><p class="simpara">A map of Throwable class names in the key and a boolean in the value.
Specify those exceptions (and subclasses) that will or won&#8217;t be retried.
Also see <code class="literal">defaultRetriable</code>.
Example: <code class="literal">spring.cloud.stream.bindings.input.consumer.retryable-exceptions.java.lang.IllegalStateException=false</code>.</p><p class="simpara">Default: empty.</p></dd><dt><span class="term">useNativeDecoding</span></dt><dd><p class="simpara">When set to <code class="literal">true</code>, the inbound message is deserialized directly by the client library, which must be configured correspondingly (for example, setting an appropriate Kafka producer value deserializer).
When this configuration is being used, the inbound message unmarshalling is not based on the <code class="literal">contentType</code> of the binding.
When native decoding is used, it is the responsibility of the producer to use an appropriate encoder (for example, the Kafka producer value serializer) to serialize the outbound message.
Also, when native encoding and decoding is used, the <code class="literal">headerMode=embeddedHeaders</code> property is ignored and headers are not embedded in the message.
See the producer property <code class="literal">useNativeEncoding</code>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_producer_properties" href="#_producer_properties"></a>30.2.3&nbsp;Producer Properties</h3></div></div></div><p>These properties are exposed via <code class="literal">org.springframework.cloud.stream.binder.ProducerProperties</code></p><p>The following binding properties are available for output bindings only and must be prefixed with <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.producer.</code> (for example, <code class="literal">spring.cloud.stream.bindings.input.producer.partitionKeyExpression=payload.id</code>).</p><p>Default values can be set by using the prefix <code class="literal">spring.cloud.stream.default.producer</code> (for example, <code class="literal">spring.cloud.stream.default.producer.partitionKeyExpression=payload.id</code>).</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">partitionKeyExpression</span></dt><dd><p class="simpara">A SpEL expression that determines how to partition outbound data.
If set, or if <code class="literal">partitionKeyExtractorClass</code> is set, outbound data on this channel is partitioned. <code class="literal">partitionCount</code> must be set to a value greater than 1 to be effective.
Mutually exclusive with <code class="literal">partitionKeyExtractorClass</code>.
See &#8220;<a class="xref" href="#partitioning" title="27.6&nbsp;Partitioning Support">Section&nbsp;27.6, &#8220;Partitioning Support&#8221;</a>&#8221;.</p><p class="simpara">Default: null.</p></dd><dt><span class="term">partitionKeyExtractorClass</span></dt><dd><p class="simpara">A <code class="literal">PartitionKeyExtractorStrategy</code> implementation.
If set, or if <code class="literal">partitionKeyExpression</code> is set, outbound data on this channel is partitioned. <code class="literal">partitionCount</code> must be set to a value greater than 1 to be effective.
Mutually exclusive with <code class="literal">partitionKeyExpression</code>.
See &#8220;<a class="xref" href="#partitioning" title="27.6&nbsp;Partitioning Support">Section&nbsp;27.6, &#8220;Partitioning Support&#8221;</a>&#8221;.</p><p class="simpara">Default: <code class="literal">null</code>.</p></dd><dt><span class="term">partitionSelectorClass</span></dt><dd><p class="simpara"> A <code class="literal">PartitionSelectorStrategy</code> implementation.
Mutually exclusive with <code class="literal">partitionSelectorExpression</code>.
If neither is set, the partition is selected as the <code class="literal">hashCode(key) % partitionCount</code>, where <code class="literal">key</code> is computed through either <code class="literal">partitionKeyExpression</code> or <code class="literal">partitionKeyExtractorClass</code>.</p><p class="simpara">Default: <code class="literal">null</code>.</p></dd><dt><span class="term">partitionSelectorExpression</span></dt><dd><p class="simpara">A SpEL expression for customizing partition selection.
Mutually exclusive with <code class="literal">partitionSelectorClass</code>.
If neither is set, the partition is selected as the <code class="literal">hashCode(key) % partitionCount</code>, where <code class="literal">key</code> is computed through either <code class="literal">partitionKeyExpression</code> or <code class="literal">partitionKeyExtractorClass</code>.</p><p class="simpara">Default: <code class="literal">null</code>.</p></dd><dt><span class="term">partitionCount</span></dt><dd><p class="simpara">The number of target partitions for the data, if partitioning is enabled.
Must be set to a value greater than 1 if the producer is partitioned.
On Kafka, it is interpreted as a hint. The larger of this and the partition count of the target topic is used instead.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">requiredGroups</span></dt><dd>A comma-separated list of groups to which the producer must ensure message delivery even if they start after it has been created (for example, by pre-creating durable queues in RabbitMQ).</dd><dt><span class="term">headerMode</span></dt><dd><p class="simpara">When set to <code class="literal">none</code>, it disables header embedding on output.
It is effective only for messaging middleware that does not support message headers natively and requires header embedding.
This option is useful when producing data for non-Spring Cloud Stream applications when native headers are not supported.
When set to <code class="literal">headers</code>, it uses the middleware&#8217;s native header mechanism.
When set to <code class="literal">embeddedHeaders</code>, it embeds headers into the message payload.</p><p class="simpara">Default: Depends on the binder implementation.</p></dd><dt><span class="term">useNativeEncoding</span></dt><dd><p class="simpara">When set to <code class="literal">true</code>, the outbound message is serialized directly by the client library, which must be configured correspondingly (for example, setting an appropriate Kafka producer value serializer).
When this configuration is being used, the outbound message marshalling is not based on the <code class="literal">contentType</code> of the binding.
When native encoding is used, it is the responsibility of the consumer to use an appropriate decoder (for example, the Kafka consumer value de-serializer) to deserialize the inbound message.
Also, when native encoding and decoding is used, the <code class="literal">headerMode=embeddedHeaders</code> property is ignored and headers are not embedded in the message.
See the consumer property <code class="literal">useNativeDecoding</code>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">errorChannelEnabled</span></dt><dd><p class="simpara">When set to <code class="literal">true</code>, if the binder supports asynchroous send results, send failures are sent to an error channel for the destination.
See &#8220;<a class="xref" href="#">???</a>&#8221; for more information.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd></dl></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dynamicdestination" href="#dynamicdestination"></a>30.3&nbsp;Using Dynamically Bound Destinations</h2></div></div></div><p>Besides the channels defined by using <code class="literal">@EnableBinding</code>, Spring Cloud Stream lets applications send messages to dynamically bound destinations.
This is useful, for example, when the target destination needs to be determined at runtime.
Applications can do so by using the <code class="literal">BinderAwareChannelResolver</code> bean, registered automatically by the <code class="literal">@EnableBinding</code> annotation.</p><p>The 'spring.cloud.stream.dynamicDestinations' property can be used for restricting the dynamic destination names to a known set (whitelisting).
If this property is not set, any destination can be bound dynamically.</p><p>The <code class="literal">BinderAwareChannelResolver</code> can be used directly, as shown in the following example of a REST controller using a path variable to decide the target channel:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding</span></em>
<em><span class="hl-annotation" style="color: gray">@Controller</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SourceWithDynamicDestination {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> BinderAwareChannelResolver resolver;
<em><span class="hl-annotation" style="color: gray">@RequestMapping(path = "/{target}", method = POST, consumes = "*/*")</span></em>
<em><span class="hl-annotation" style="color: gray">@ResponseStatus(HttpStatus.ACCEPTED)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handleRequest(<em><span class="hl-annotation" style="color: gray">@RequestBody</span></em> String body, <em><span class="hl-annotation" style="color: gray">@PathVariable("target")</span></em> target,
<em><span class="hl-annotation" style="color: gray">@RequestHeader(HttpHeaders.CONTENT_TYPE)</span></em> Object contentType) {
sendMessage(body, target, contentType);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> sendMessage(String body, String target, Object contentType) {
resolver.resolveDestination(target).send(MessageBuilder.createMessage(body,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
}
}</pre><p>Now consider what happens when we start the application on the default port (8080) and make the following requests with CURL:</p><pre class="screen">curl -H "Content-Type: application/json" -X POST -d "customer-1" http://localhost:8080/customers
curl -H "Content-Type: application/json" -X POST -d "order-1" http://localhost:8080/orders</pre><p>The destinations, 'customers' and 'orders', are created in the broker (in the exchange for Rabbit or in the topic for Kafka) with names of 'customers' and 'orders', and the data is published to the appropriate destinations.</p><p>The <code class="literal">BinderAwareChannelResolver</code> is a general-purpose Spring Integration <code class="literal">DestinationResolver</code> and can be injected in other components&#8201;&#8212;&#8201;for example, in a router using a SpEL expression based on the <code class="literal">target</code> field of an incoming JSON message. The following example includes a router that reads SpEL expressions:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding</span></em>
<em><span class="hl-annotation" style="color: gray">@Controller</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SourceWithDynamicDestination {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> BinderAwareChannelResolver resolver;
<em><span class="hl-annotation" style="color: gray">@RequestMapping(path = "/", method = POST, consumes = "application/json")</span></em>
<em><span class="hl-annotation" style="color: gray">@ResponseStatus(HttpStatus.ACCEPTED)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handleRequest(<em><span class="hl-annotation" style="color: gray">@RequestBody</span></em> String body, <em><span class="hl-annotation" style="color: gray">@RequestHeader(HttpHeaders.CONTENT_TYPE)</span></em> Object contentType) {
sendMessage(body, contentType);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> sendMessage(Object body, Object contentType) {
routerChannel().send(MessageBuilder.createMessage(body,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
}
<em><span class="hl-annotation" style="color: gray">@Bean(name = "routerChannel")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> MessageChannel routerChannel() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> DirectChannel();
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@ServiceActivator(inputChannel = "routerChannel")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ExpressionEvaluatingRouter router() {
ExpressionEvaluatingRouter router =
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ExpressionEvaluatingRouter(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpelExpressionParser().parseExpression(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"payload.target"</span>));
router.setDefaultOutputChannelName(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"default-output"</span>);
router.setChannelResolver(resolver);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> router;
}
}</pre><p>The <a class="link" href="https://github.com/spring-cloud-stream-app-starters/router" target="_top">Router Sink Application</a> uses this technique to create the destinations on-demand.</p><p>If the channel names are known in advance, you can configure the producer properties as with any other destination.
Alternatively, if you register a <code class="literal">NewBindingCallback&lt;&gt;</code> bean, it is invoked just before the binding is created.
The callback takes the generic type of the extended producer properties used by the binder.
It has one method:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> configure(String channelName, MessageChannel channel, ProducerProperties producerProperties,
T extendedProducerProperties);</pre><p>The following example shows how to use the RabbitMQ binder:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> NewBindingCallback&lt;RabbitProducerProperties&gt; dynamicConfigurer() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (name, channel, props, extended) -&gt; {
props.setRequiredGroups(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bindThisQueue"</span>);
extended.setQueueNameGroupOnly(true);
extended.setAutoBindDlq(true);
extended.setDeadLetterQueueName(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myDLQ"</span>);
};
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you need to support dynamic destinations with multiple binder types, use <code class="literal">Object</code> for the generic type and cast the <code class="literal">extended</code> argument as needed.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="content-type-management" href="#content-type-management"></a>31.&nbsp;Content Type Negotiation</h2></div></div></div><p>Data transformation is one of the core features of any message-driven microservice architecture. Given that, in Spring Cloud Stream, such data
is represented as a Spring <code class="literal">Message</code>, a message may have to be transformed to a desired shape or size before reaching its destination. This is required for two reasons:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">To convert the contents of the incoming message to match the signature of the application-provided handler.</li><li class="listitem">To convert the contents of the outgoing message to the wire format.</li></ol></div><p>The wire format is typically <code class="literal">byte[]</code> (that is true for the Kafka and Rabbit binders), but it is governed by the binder implementation.</p><p>In Spring Cloud Stream, message transformation is accomplished with an <code class="literal">org.springframework.messaging.converter.MessageConverter</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>As a supplement to the details to follow, you may also want to read the following <a class="link" href="https://spring.io/blog/2018/02/26/spring-cloud-stream-2-0-content-type-negotiation-and-transformation" target="_top">blog post</a>.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_mechanics" href="#_mechanics"></a>31.1&nbsp;Mechanics</h2></div></div></div><p>To better understand the mechanics and the necessity behind content-type negotiation, we take a look at a very simple use case by using the following message handler as an example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@StreamListener(Processor.INPUT)</span></em>
<em><span class="hl-annotation" style="color: gray">@SendTo(Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String handle(Person person) {..}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>For simplicity, we assume that this is the only handler in the application (we assume there is no internal pipeline).</p></td></tr></table></div><p>The handler shown in the preceding example expects a <code class="literal">Person</code> object as an argument and produces a <code class="literal">String</code> type as an output.
In order for the framework to succeed in passing the incoming <code class="literal">Message</code> as an argument to this handler, it has to somehow transform the payload of the <code class="literal">Message</code> type from the wire format to a <code class="literal">Person</code> type.
In other words, the framework must locate and apply the appropriate <code class="literal">MessageConverter</code>.
To accomplish that, the framework needs some instructions from the user.
One of these instructions is already provided by the signature of the handler method itself (<code class="literal">Person</code> type).
Consequently, in theory, that should be (and, in some cases, is) enough.
However, for the majority of use cases, in order to select the appropriate <code class="literal">MessageConverter</code>, the framework needs an additional piece of information.
That missing piece is <code class="literal">contentType</code>.</p><p>Spring Cloud Stream provides three mechanisms to define <code class="literal">contentType</code> (in order of precedence):</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><span class="strong"><strong>HEADER</strong></span>: The <code class="literal">contentType</code> can be communicated through the Message itself. By providing a <code class="literal">contentType</code> header, you declare the content type to use to locate and apply the appropriate <code class="literal">MessageConverter</code>.</li><li class="listitem"><p class="simpara"><span class="strong"><strong>BINDING</strong></span>: The <code class="literal">contentType</code> can be set per destination binding by setting the <code class="literal">spring.cloud.stream.bindings.input.content-type</code> property.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">input</code> segment in the property name corresponds to the actual name of the destination (which is &#8220;input&#8221; in our case). This approach lets you declare, on a per-binding basis, the content type to use to locate and apply the appropriate <code class="literal">MessageConverter</code>.</p></td></tr></table></div></li><li class="listitem"><span class="strong"><strong>DEFAULT</strong></span>: If <code class="literal">contentType</code> is not present in the <code class="literal">Message</code> header or the binding, the default <code class="literal">application/json</code> content type is used to
locate and apply the appropriate <code class="literal">MessageConverter</code>.</li></ol></div><p>As mentioned earlier, the preceding list also demonstrates the order of precedence in case of a tie. For example, a header-provided content type takes precedence over any other content type.
The same applies for a content type set on a per-binding basis, which essentially lets you override the default content type.
However, it also provides a sensible default (which was determined from community feedback).</p><p>Another reason for making <code class="literal">application/json</code> the default stems from the interoperability requirements driven by distributed microservices architectures, where producer and consumer not only run in different JVMs but can also run on different non-JVM platforms.</p><p>When the non-void handler method returns, if the the return value is already a <code class="literal">Message</code>, that <code class="literal">Message</code> becomes the payload. However, when the return value is not a <code class="literal">Message</code>, the new <code class="literal">Message</code> is constructed with the return value as the payload while inheriting
headers from the input <code class="literal">Message</code> minus the headers defined or filtered by <code class="literal">SpringIntegrationProperties.messageHandlerNotPropagatedHeaders</code>.
By default, there is only one header set there: <code class="literal">contentType</code>. This means that the new <code class="literal">Message</code> does not have <code class="literal">contentType</code> header set, thus ensuring that the <code class="literal">contentType</code> can evolve.
You can always opt out of returning a <code class="literal">Message</code> from the handler method where you can inject any header you wish.</p><p>If there is an internal pipeline, the <code class="literal">Message</code> is sent to the next handler by going through the same process of conversion. However, if there is no internal pipeline or you have reached the end of it, the <code class="literal">Message</code> is sent back to the output destination.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_content_type_versus_argument_type" href="#_content_type_versus_argument_type"></a>31.1.1&nbsp;Content Type versus Argument Type</h3></div></div></div><p>As mentioned earlier, for the framework to select the appropriate <code class="literal">MessageConverter</code>, it requires argument type and, optionally, content type information.
The logic for selecting the appropriate <code class="literal">MessageConverter</code> resides with the argument resolvers (<code class="literal">HandlerMethodArgumentResolvers</code>), which trigger right before the invocation of the user-defined handler method (which is when the actual argument type is known to the framework).
If the argument type does not match the type of the current payload, the framework delegates to the stack of the
pre-configured <code class="literal">MessageConverters</code> to see if any one of them can convert the payload.
As you can see, the <code class="literal">Object fromMessage(Message&lt;?&gt; message, Class&lt;?&gt; targetClass);</code>
operation of the MessageConverter takes <code class="literal">targetClass</code> as one of its arguments.
The framework also ensures that the provided <code class="literal">Message</code> always contains a <code class="literal">contentType</code> header.
When no contentType header was already present, it injects either the per-binding <code class="literal">contentType</code> header or the default <code class="literal">contentType</code> header.
The combination of <code class="literal">contentType</code> argument type is the mechanism by which framework determines if message can be converted to a target type.
If no appropriate <code class="literal">MessageConverter</code> is found, an exception is thrown, which you can handle by adding a custom <code class="literal">MessageConverter</code> (see &#8220;<a class="xref" href="#spring-cloud-stream-overview-user-defined-message-converters" title="31.3&nbsp;User-defined Message Converters">Section&nbsp;31.3, &#8220;User-defined Message Converters&#8221;</a>&#8221;).</p><p>But what if the payload type matches the target type declared by the handler method? In this case, there is nothing to convert, and the
payload is passed unmodified. While this sounds pretty straightforward and logical, keep in mind handler methods that take a <code class="literal">Message&lt;?&gt;</code> or <code class="literal">Object</code> as an argument.
By declaring the target type to be <code class="literal">Object</code> (which is an <code class="literal">instanceof</code> everything in Java), you essentially forfeit the conversion process.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Do not expect <code class="literal">Message</code> to be converted into some other type based only on the <code class="literal">contentType</code>.
Remember that the <code class="literal">contentType</code> is complementary to the target type.
If you wish, you can provide a hint, which <code class="literal">MessageConverter</code> may or may not take into consideration.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_message_converters" href="#_message_converters"></a>31.1.2&nbsp;Message Converters</h3></div></div></div><p><code class="literal">MessageConverters</code> define two methods:</p><pre class="programlisting">Object fromMessage(Message&lt;?&gt; message, Class&lt;?&gt; targetClass);
Message&lt;?&gt; toMessage(Object payload, <em><span class="hl-annotation" style="color: gray">@Nullable</span></em> MessageHeaders headers);</pre><p>It is important to understand the contract of these methods and their usage, specifically in the context of Spring Cloud Stream.</p><p>The <code class="literal">fromMessage</code> method converts an incoming <code class="literal">Message</code> to an argument type.
The payload of the <code class="literal">Message</code> could be any type, and it is
up to the actual implementation of the <code class="literal">MessageConverter</code> to support multiple types.
For example, some JSON converter may support the payload type as <code class="literal">byte[]</code>, <code class="literal">String</code>, and others.
This is important when the application contains an internal pipeline (that is, input &#8594; handler1 &#8594; handler2 &#8594;. . . &#8594; output) and the output of the upstream handler results in a <code class="literal">Message</code> which may not be in the initial wire format.</p><p>However, the <code class="literal">toMessage</code> method has a more strict contract and must always convert <code class="literal">Message</code> to the wire format: <code class="literal">byte[]</code>.</p><p>So, for all intents and purposes (and especially when implementing your own converter) you regard the two methods as having the following signatures:</p><pre class="programlisting">Object fromMessage(Message&lt;?&gt; message, Class&lt;?&gt; targetClass);
Message&lt;<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]&gt; toMessage(Object payload, <em><span class="hl-annotation" style="color: gray">@Nullable</span></em> MessageHeaders headers);</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_provided_messageconverters" href="#_provided_messageconverters"></a>31.2&nbsp;Provided MessageConverters</h2></div></div></div><p>As mentioned earlier, the framework already provides a stack of <code class="literal">MessageConverters</code> to handle most common use cases.
The following list describes the provided <code class="literal">MessageConverters</code>, in order of precedence (the first <code class="literal">MessageConverter</code> that works is used):</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><code class="literal">ApplicationJsonMessageMarshallingConverter</code>: Variation of the <code class="literal">org.springframework.messaging.converter.MappingJackson2MessageConverter</code>. Supports conversion of the payload of the <code class="literal">Message</code> to/from POJO for cases when <code class="literal">contentType</code> is <code class="literal">application/json</code> (DEFAULT).</li><li class="listitem"><code class="literal">TupleJsonMessageConverter</code>: <span class="strong"><strong>DEPRECATED</strong></span> Supports conversion of the payload of the <code class="literal">Message</code> to/from <code class="literal">org.springframework.tuple.Tuple</code>.</li><li class="listitem"><code class="literal">ByteArrayMessageConverter</code>: Supports conversion of the payload of the <code class="literal">Message</code> from <code class="literal">byte[]</code> to <code class="literal">byte[]</code> for cases when <code class="literal">contentType</code> is <code class="literal">application/octet-stream</code>. It is essentially a pass through and exists primarily for backward compatibility.</li><li class="listitem"><code class="literal">ObjectStringMessageConverter</code>: Supports conversion of any type to a <code class="literal">String</code> when <code class="literal">contentType</code> is <code class="literal">text/plain</code>.
It invokes Object&#8217;s <code class="literal">toString()</code> method or, if the payload is <code class="literal">byte[]</code>, a new <code class="literal">String(byte[])</code>.</li><li class="listitem"><code class="literal">JavaSerializationMessageConverter</code>: <span class="strong"><strong>DEPRECATED</strong></span> Supports conversion based on java serialization when <code class="literal">contentType</code> is <code class="literal">application/x-java-serialized-object</code>.</li><li class="listitem"><code class="literal">KryoMessageConverter</code>: <span class="strong"><strong>DEPRECATED</strong></span> Supports conversion based on Kryo serialization when <code class="literal">contentType</code> is <code class="literal">application/x-java-object</code>.</li><li class="listitem"><code class="literal">JsonUnmarshallingConverter</code>: Similar to the <code class="literal">ApplicationJsonMessageMarshallingConverter</code>. It supports conversion of any type when <code class="literal">contentType</code> is <code class="literal">application/x-java-object</code>.
It expects the actual type information to be embedded in the <code class="literal">contentType</code> as an attribute (for example, <code class="literal">application/x-java-object;type=foo.bar.Cat</code>).</li></ol></div><p>When no appropriate converter is found, the framework throws an exception. When that happens, you should check your code and configuration and ensure you did not miss anything (that is, ensure that you provided a <code class="literal">contentType</code> by using a binding or a header).
However, most likely, you found some uncommon case (such as a custom <code class="literal">contentType</code> perhaps) and the current stack of provided <code class="literal">MessageConverters</code>
does not know how to convert. If that is the case, you can add custom <code class="literal">MessageConverter</code>. See <a class="xref" href="#spring-cloud-stream-overview-user-defined-message-converters" title="31.3&nbsp;User-defined Message Converters">Section&nbsp;31.3, &#8220;User-defined Message Converters&#8221;</a>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-user-defined-message-converters" href="#spring-cloud-stream-overview-user-defined-message-converters"></a>31.3&nbsp;User-defined Message Converters</h2></div></div></div><p>Spring Cloud Stream exposes a mechanism to define and register additional <code class="literal">MessageConverters</code>.
To use it, implement <code class="literal">org.springframework.messaging.converter.MessageConverter</code>, configure it as a <code class="literal">@Bean</code>, and annotate it with <code class="literal">@StreamMessageConverter</code>.
It is then apended to the existing stack of `MessageConverter`s.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>It is important to understand that custom <code class="literal">MessageConverter</code> implementations are added to the head of the existing stack.
Consequently, custom <code class="literal">MessageConverter</code> implementations take precedence over the existing ones, which lets you override as well as add to the existing converters.</p></td></tr></table></div><p>The following example shows how to create a message converter bean to support a new content type called <code class="literal">application/bar</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SinkApplication {
...
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@StreamMessageConverter</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> MessageConverter customMessageConverter() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> MyCustomMessageConverter();
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyCustomMessageConverter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AbstractMessageConverter {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> MyCustomMessageConverter() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> MimeType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>));
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> supports(Class&lt;?&gt; clazz) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (Bar.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>.equals(clazz));
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> Object convertFromInternal(Message&lt;?&gt; message, Class&lt;?&gt; targetClass, Object conversionHint) {
Object payload = message.getPayload();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (payload <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">instanceof</span> Bar ? payload : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Bar((<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]) payload));
}
}</pre><p>Spring Cloud Stream also provides support for Avro-based converters and schema evolution.
See &#8220;<a class="xref" href="#schema-evolution" title="32.&nbsp;Schema Evolution Support">Chapter&nbsp;32, <i>Schema Evolution Support</i></a>&#8221; for details.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="schema-evolution" href="#schema-evolution"></a>32.&nbsp;Schema Evolution Support</h2></div></div></div><p>Spring Cloud Stream provides support for schema evolution so that the data can be evolved over time and still work with older or newer producers and consumers and vice versa.
Most serialization models, especially the ones that aim for portability across different platforms and languages, rely on a schema that describes how the data is serialized in the binary payload.
In order to serialize the data and then to interpret it, both the sending and receiving sides must have access to a schema that describes the binary format.
In certain cases, the schema can be inferred from the payload type on serialization or from the target type on deserialization.
However, many applications benefit from having access to an explicit schema that describes the binary data format.
A schema registry lets you store schema information in a textual format (typically JSON) and makes that information accessible to various applications that need it to receive and send data in binary format.
A schema is referenceable as a tuple consisting of:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">A subject that is the logical name of the schema</li><li class="listitem">The schema version</li><li class="listitem">The schema format, which describes the binary format of the data</li></ul></div><p>This following sections goes through the details of various components involved in schema evolution process.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_schema_registry_client" href="#_schema_registry_client"></a>32.1&nbsp;Schema Registry Client</h2></div></div></div><p>The client-side abstraction for interacting with schema registry servers is the <code class="literal">SchemaRegistryClient</code> interface, which has the following structure:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> SchemaRegistryClient {
SchemaRegistrationResponse register(String subject, String format, String schema);
String fetch(SchemaReference schemaReference);
String fetch(Integer id);
}</pre><p>Spring Cloud Stream provides out-of-the-box implementations for interacting with its own schema server and for interacting with the Confluent Schema Registry.</p><p>A client for the Spring Cloud Stream schema registry can be configured by using the <code class="literal">@EnableSchemaRegistryClient</code>, as follows:</p><pre class="programlisting"> <em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableSchemaRegistryClient</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> AvroSinkApplication {
...
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The default converter is optimized to cache not only the schemas from the remote server but also the <code class="literal">parse()</code> and <code class="literal">toString()</code> methods, which are quite expensive.
Because of this, it uses a <code class="literal">DefaultSchemaRegistryClient</code> that does not cache responses.
If you intend to change the default behavior, you can use the client directly on your code and override it to the desired outcome.
To do so, you have to add the property <code class="literal">spring.cloud.stream.schemaRegistryClient.cached=true</code> to your application properties.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_schema_registry_client_properties" href="#_schema_registry_client_properties"></a>32.1.1&nbsp;Schema Registry Client Properties</h3></div></div></div><p>The Schema Registry Client supports the following properties:</p><div class="variablelist"><dl class="variablelist"><dt><span class="term"><code class="literal">spring.cloud.stream.schemaRegistryClient.endpoint</code></span></dt><dd>The location of the schema-server.
When setting this, use a full URL, including protocol (<code class="literal">http</code> or <code class="literal">https</code>) , port, and context path.</dd><dt><span class="term">Default</span></dt><dd><code class="literal"><a class="link" href="http://localhost:8990/" target="_top">http://localhost:8990/</a></code></dd><dt><span class="term"><code class="literal">spring.cloud.stream.schemaRegistryClient.cached</code></span></dt><dd>Whether the client should cache schema server responses.
Normally set to <code class="literal">false</code>, as the caching happens in the message converter.
Clients using the schema registry client should set this to <code class="literal">true</code>.</dd><dt><span class="term">Default</span></dt><dd><code class="literal">true</code></dd></dl></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_avro_schema_registry_client_message_converters" href="#_avro_schema_registry_client_message_converters"></a>32.2&nbsp;Avro Schema Registry Client Message Converters</h2></div></div></div><p>For applications that have a SchemaRegistryClient bean registered with the application context, Spring Cloud Stream auto configures an Apache Avro message converter for schema management.
This eases schema evolution, as applications that receive messages can get easy access to a writer schema that can be reconciled with their own reader schema.</p><p>For outbound messages, if the content type of the channel is set to <code class="literal">application/*+avro</code>, the <code class="literal">MessageConverter</code> is activated, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.stream.bindings.output.contentType</span>=application/*+avro</pre><p>During the outbound conversion, the message converter tries to infer the schema of each outbound messages (based on its type) and register it to a subject (based on the payload type) by using the <code class="literal">SchemaRegistryClient</code>.
If an identical schema is already found, then a reference to it is retrieved.
If not, the schema is registered, and a new version number is provided.
The message is sent with a <code class="literal">contentType</code> header by using the following scheme: <code class="literal">application/[prefix].[subject].v[version]+avro</code>, where <code class="literal">prefix</code> is configurable and <code class="literal">subject</code> is deduced from the payload type.</p><p>For example, a message of the type <code class="literal">User</code> might be sent as a binary payload with a content type of <code class="literal">application/vnd.user.v2+avro</code>, where <code class="literal">user</code> is the subject and <code class="literal">2</code> is the version number.</p><p>When receiving messages, the converter infers the schema reference from the header of the incoming message and tries to retrieve it. The schema is used as the writer schema in the deserialization process.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_avro_schema_registry_message_converter_properties" href="#_avro_schema_registry_message_converter_properties"></a>32.2.1&nbsp;Avro Schema Registry Message Converter Properties</h3></div></div></div><p>If you have enabled Avro based schema registry client by setting <code class="literal">spring.cloud.stream.bindings.output.contentType=application/*+avro</code>, you can customize the behavior of the registration by setting the following properties.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled</span></dt><dd><p class="simpara">Enable if you want the converter to use reflection to infer a Schema from a POJO.</p><p class="simpara">Default: <code class="literal">false</code></p></dd><dt><span class="term">spring.cloud.stream.schema.avro.readerSchema</span></dt><dd>Avro compares schema versions by looking at a writer schema (origin payload) and a reader schema (your application payload). See the <a class="link" href="https://avro.apache.org/docs/1.7.6/spec.html" target="_top">Avro documentation</a> for more information. If set, this overrides any lookups at the schema server and uses the local schema as the reader schema.
Default: <code class="literal">null</code></dd><dt><span class="term">spring.cloud.stream.schema.avro.schemaLocations</span></dt><dd><p class="simpara">Registers any <code class="literal">.avsc</code> files listed in this property with the Schema Server.</p><p class="simpara">Default: <code class="literal">empty</code></p></dd><dt><span class="term">spring.cloud.stream.schema.avro.prefix</span></dt><dd><p class="simpara">The prefix to be used on the Content-Type header.</p><p class="simpara">Default: <code class="literal">vnd</code></p></dd></dl></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_apache_avro_message_converters" href="#_apache_avro_message_converters"></a>32.3&nbsp;Apache Avro Message Converters</h2></div></div></div><p>Spring Cloud Stream provides support for schema-based message converters through its <code class="literal">spring-cloud-stream-schema</code> module.
Currently, the only serialization format supported out of the box for schema-based message converters is Apache Avro, with more formats to be added in future versions.</p><p>The <code class="literal">spring-cloud-stream-schema</code> module contains two types of message converters that can be used for Apache Avro serialization:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Converters that use the class information of the serialized or deserialized objects or a schema with a location known at startup.</li><li class="listitem">Converters that use a schema registry. They locate the schemas at runtime and dynamically register new schemas as domain objects evolve.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_converters_with_schema_support" href="#_converters_with_schema_support"></a>32.4&nbsp;Converters with Schema Support</h2></div></div></div><p>The <code class="literal">AvroSchemaMessageConverter</code> supports serializing and deserializing messages either by using a predefined schema or by using the schema information available in the class (either reflectively or contained in the <code class="literal">SpecificRecord</code>).
If you provide a custom converter, then the default AvroSchemaMessageConverter bean is not created. The following example shows a custom converter:</p><p>To use custom converters, you can simply add it to the application context, optionally specifying one or more <code class="literal">MimeTypes</code> with which to associate it.
The default <code class="literal">MimeType</code> is <code class="literal">application/avro</code>.</p><p>If the target type of the conversion is a <code class="literal">GenericRecord</code>, a schema must be set.</p><p>The following example shows how to configure a converter in a sink application by registering the Apache Avro <code class="literal">MessageConverter</code> without a predefined schema.
In this example, note that the mime type value is <code class="literal">avro/bytes</code>, not the default <code class="literal">application/avro</code>.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SinkApplication {
...
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> MessageConverter userMessageConverter() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AvroSchemaMessageConverter(MimeType.valueOf(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"avro/bytes"</span>));
}
}</pre><p>Conversely, the following application registers a converter with a predefined schema (found on the classpath):</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SinkApplication {
...
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> MessageConverter userMessageConverter() {
AvroSchemaMessageConverter converter = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AvroSchemaMessageConverter(MimeType.valueOf(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"avro/bytes"</span>));
converter.setSchemaLocation(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ClassPathResource(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"schemas/User.avro"</span>));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> converter;
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_schema_registry_server" href="#_schema_registry_server"></a>32.5&nbsp;Schema Registry Server</h2></div></div></div><p>Spring Cloud Stream provides a schema registry server implementation.
To use it, you can add the <code class="literal">spring-cloud-stream-schema-server</code> artifact to your project and use the <code class="literal">@EnableSchemaRegistryServer</code> annotation, which adds the schema registry server REST controller to your application.
This annotation is intended to be used with Spring Boot web applications, and the listening port of the server is controlled by the <code class="literal">server.port</code> property.
The <code class="literal">spring.cloud.stream.schema.server.path</code> property can be used to control the root path of the schema server (especially when it is embedded in other applications).
The <code class="literal">spring.cloud.stream.schema.server.allowSchemaDeletion</code> boolean property enables the deletion of a schema. By default, this is disabled.</p><p>The schema registry server uses a relational database to store the schemas.
By default, it uses an embedded database.
You can customize the schema storage by using the <a class="link" href="http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-sql" target="_top">Spring Boot SQL database and JDBC configuration options</a>.</p><p>The following example shows a Spring Boot application that enables the schema registry:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableSchemaRegistryServer</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SchemaRegistryServerApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(SchemaRegistryServerApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_schema_registry_server_api" href="#_schema_registry_server_api"></a>32.5.1&nbsp;Schema Registry Server API</h3></div></div></div><p>The Schema Registry Server API consists of the following operations:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">POST /</code>&#8201;&#8212;&#8201;see &#8220;<a class="xref" href="#spring-cloud-stream-overview-registering-new-schema" title="Registering a New Schema">the section called &#8220;Registering a New Schema&#8221;</a>&#8221;</li><li class="listitem">'GET /{subject}/{format}/{version}'&#8201;&#8212;&#8201;see &#8220;<a class="xref" href="#spring-cloud-stream-overview-retrieve-schema-subject-format-version" title="Retrieving an Existing Schema by Subject, Format, and Version">the section called &#8220;Retrieving an Existing Schema by Subject, Format, and Version&#8221;</a>&#8221;</li><li class="listitem"><code class="literal">GET /{subject}/{format}</code>&#8201;&#8212;&#8201;see &#8220;<a class="xref" href="#spring-cloud-stream-overview-retrieve-schema-subject-format" title="Retrieving an Existing Schema by Subject and Format">the section called &#8220;Retrieving an Existing Schema by Subject and Format&#8221;</a>&#8221;</li><li class="listitem"><code class="literal">GET /schemas/{id}</code>&#8201;&#8212;&#8201;see &#8220;<a class="xref" href="#spring-cloud-stream-overview-retrieve-schema-id" title="Retrieving an Existing Schema by ID">the section called &#8220;Retrieving an Existing Schema by ID&#8221;</a>&#8221;</li><li class="listitem"><code class="literal">DELETE /{subject}/{format}/{version}</code>&#8201;&#8212;&#8201;see &#8220;<a class="xref" href="#spring-cloud-stream-overview-deleting-schema-subject-format-version" title="Deleting a Schema by Subject, Format, and Version">the section called &#8220;Deleting a Schema by Subject, Format, and Version&#8221;</a>&#8221;</li><li class="listitem"><code class="literal">DELETE /schemas/{id}</code>&#8201;&#8212;&#8201;see &#8220;<a class="xref" href="#spring-cloud-stream-overview-deleting-schema-id" title="Deleting a Schema by ID">the section called &#8220;Deleting a Schema by ID&#8221;</a>&#8221;</li><li class="listitem"><code class="literal">DELETE /{subject}</code>&#8201;&#8212;&#8201;see &#8220;<a class="xref" href="#spring-cloud-stream-overview-deleting-schema-subject" title="Deleting a Schema by Subject">the section called &#8220;Deleting a Schema by Subject&#8221;</a>&#8221;</li></ul></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-stream-overview-registering-new-schema" href="#spring-cloud-stream-overview-registering-new-schema"></a>Registering a New Schema</h4></div></div></div><p>To register a new schema, send a <code class="literal">POST</code> request to the <code class="literal">/</code> endpoint.</p><p>The <code class="literal">/</code> accepts a JSON payload with the following fields:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">subject</code>: The schema subject</li><li class="listitem"><code class="literal">format</code>: The schema format</li><li class="listitem"><code class="literal">definition</code>: The schema definition</li></ul></div><p>Its response is a schema object in JSON, with the following fields:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">id</code>: The schema ID</li><li class="listitem"><code class="literal">subject</code>: The schema subject</li><li class="listitem"><code class="literal">format</code>: The schema format</li><li class="listitem"><code class="literal">version</code>: The schema version</li><li class="listitem"><code class="literal">definition</code>: The schema definition</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-stream-overview-retrieve-schema-subject-format-version" href="#spring-cloud-stream-overview-retrieve-schema-subject-format-version"></a>Retrieving an Existing Schema by Subject, Format, and Version</h4></div></div></div><p>To retrieve an existing schema by subject, format, and version, send <code class="literal">GET</code> request to the <code class="literal">/{subject}/{format}/{version}</code> endpoint.</p><p>Its response is a schema object in JSON, with the following fields:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">id</code>: The schema ID</li><li class="listitem"><code class="literal">subject</code>: The schema subject</li><li class="listitem"><code class="literal">format</code>: The schema format</li><li class="listitem"><code class="literal">version</code>: The schema version</li><li class="listitem"><code class="literal">definition</code>: The schema definition</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-stream-overview-retrieve-schema-subject-format" href="#spring-cloud-stream-overview-retrieve-schema-subject-format"></a>Retrieving an Existing Schema by Subject and Format</h4></div></div></div><p>To retrieve an existing schema by subject and format, send a <code class="literal">GET</code> request to the <code class="literal">/subject/format</code> endpoint.</p><p>Its response is a list of schemas with each schema object in JSON, with the following fields:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">id</code>: The schema ID</li><li class="listitem"><code class="literal">subject</code>: The schema subject</li><li class="listitem"><code class="literal">format</code>: The schema format</li><li class="listitem"><code class="literal">version</code>: The schema version</li><li class="listitem"><code class="literal">definition</code>: The schema definition</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-stream-overview-retrieve-schema-id" href="#spring-cloud-stream-overview-retrieve-schema-id"></a>Retrieving an Existing Schema by ID</h4></div></div></div><p>To retrieve a schema by its ID, send a <code class="literal">GET</code> request to the <code class="literal">/schemas/{id}</code> endpoint.</p><p>Its response is a schema object in JSON, with the following fields:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">id</code>: The schema ID</li><li class="listitem"><code class="literal">subject</code>: The schema subject</li><li class="listitem"><code class="literal">format</code>: The schema format</li><li class="listitem"><code class="literal">version</code>: The schema version</li><li class="listitem"><code class="literal">definition</code>: The schema definition</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-stream-overview-deleting-schema-subject-format-version" href="#spring-cloud-stream-overview-deleting-schema-subject-format-version"></a>Deleting a Schema by Subject, Format, and Version</h4></div></div></div><p>To delete a schema identified by its subject, format, and version, send a <code class="literal">DELETE</code> request to the <code class="literal">/{subject}/{format}/{version}</code> endpoint.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-stream-overview-deleting-schema-id" href="#spring-cloud-stream-overview-deleting-schema-id"></a>Deleting a Schema by ID</h4></div></div></div><p>To delete a schema by its ID, send a <code class="literal">DELETE</code> request to the <code class="literal">/schemas/{id}</code> endpoint.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-stream-overview-deleting-schema-subject" href="#spring-cloud-stream-overview-deleting-schema-subject"></a>Deleting a Schema by Subject</h4></div></div></div><p><code class="literal">DELETE /{subject}</code></p><p>Delete existing schemas by their subject.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>This note applies to users of Spring Cloud Stream 1.1.0.RELEASE only.
Spring Cloud Stream 1.1.0.RELEASE used the table name, <code class="literal">schema</code>, for storing <code class="literal">Schema</code> objects. <code class="literal">Schema</code> is a keyword in a number of database implementations.
To avoid any conflicts in the future, starting with 1.1.1.RELEASE, we have opted for the name <code class="literal">SCHEMA_REPOSITORY</code> for the storage table.
Any Spring Cloud Stream 1.1.0.RELEASE users who upgrade should migrate their existing schemas to the new table before upgrading.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_confluent_s_schema_registry" href="#_using_confluent_s_schema_registry"></a>32.5.2&nbsp;Using Confluent&#8217;s Schema Registry</h3></div></div></div><p>The default configuration creates a <code class="literal">DefaultSchemaRegistryClient</code> bean.
If you want to use the Confluent schema registry, you need to create a bean of type <code class="literal">ConfluentSchemaRegistryClient</code>, which supersedes the one configured by default by the framework. The following example shows how to create such a bean:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> SchemaRegistryClient schemaRegistryClient(<em><span class="hl-annotation" style="color: gray">@Value("${spring.cloud.stream.schemaRegistryClient.endpoint}")</span></em> String endpoint){
ConfluentSchemaRegistryClient client = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ConfluentSchemaRegistryClient();
client.setEndpoint(endpoint);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> client;
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The ConfluentSchemaRegistryClient is tested against Confluent platform version 4.0.0.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_schema_registration_and_resolution" href="#_schema_registration_and_resolution"></a>32.6&nbsp;Schema Registration and Resolution</h2></div></div></div><p>To better understand how Spring Cloud Stream registers and resolves new schemas and its use of Avro schema comparison features, we provide two separate subsections:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">&#8220;<a class="xref" href="#spring-cloud-stream-overview-schema-registration-process" title="32.6.1&nbsp;Schema Registration Process (Serialization)">Section&nbsp;32.6.1, &#8220;Schema Registration Process (Serialization)&#8221;</a>&#8221;</li><li class="listitem">&#8220;<a class="xref" href="#spring-cloud-stream-overview-schema-resolution-process" title="32.6.2&nbsp;Schema Resolution Process (Deserialization)">Section&nbsp;32.6.2, &#8220;Schema Resolution Process (Deserialization)&#8221;</a>&#8221;</li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-stream-overview-schema-registration-process" href="#spring-cloud-stream-overview-schema-registration-process"></a>32.6.1&nbsp;Schema Registration Process (Serialization)</h3></div></div></div><p>The first part of the registration process is extracting a schema from the payload that is being sent over a channel.
Avro types such as <code class="literal">SpecificRecord</code> or <code class="literal">GenericRecord</code> already contain a schema, which can be retrieved immediately from the instance.
In the case of POJOs, a schema is inferred if the <code class="literal">spring.cloud.stream.schema.avro.dynamicSchemaGenerationEnabled</code> property is set to <code class="literal">true</code> (the default).</p><div class="figure"><a name="d0e11127" href="#d0e11127"></a><p class="title"><b>Figure&nbsp;32.1.&nbsp;Schema Writer Resolution Process</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/schema_resolution.png" align="middle" alt="schema resolution"></div></div></div><br class="figure-break"><p>Ones a schema is obtained, the converter loads its metadata (version) from the remote server.
First, it queries a local cache. If no result is found, it submits the data to the server, which replies with versioning information.
The converter always caches the results to avoid the overhead of querying the Schema Server for every new message that needs to be serialized.</p><div class="figure"><a name="d0e11138" href="#d0e11138"></a><p class="title"><b>Figure&nbsp;32.2.&nbsp;Schema Registration Process</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/registration.png" align="middle" alt="registration"></div></div></div><br class="figure-break"><p>With the schema version information, the converter sets the <code class="literal">contentType</code> header of the message to carry the version information&#8201;&#8212;&#8201;for example: <code class="literal">application/vnd.user.v1+avro</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-stream-overview-schema-resolution-process" href="#spring-cloud-stream-overview-schema-resolution-process"></a>32.6.2&nbsp;Schema Resolution Process (Deserialization)</h3></div></div></div><p>When reading messages that contain version information (that is, a <code class="literal">contentType</code> header with a scheme like the one described under &#8220;<a class="xref" href="#spring-cloud-stream-overview-schema-registration-process" title="32.6.1&nbsp;Schema Registration Process (Serialization)">Section&nbsp;32.6.1, &#8220;Schema Registration Process (Serialization)&#8221;</a>&#8221;), the converter queries the Schema server to fetch the writer schema of the message.
Once it has found the correct schema of the incoming message, it retrieves the reader schema and, by using Avro&#8217;s schema resolution support, reads it into the reader definition (setting defaults and any missing properties).</p><div class="figure"><a name="d0e11165" href="#d0e11165"></a><p class="title"><b>Figure&nbsp;32.3.&nbsp;Schema Reading Resolution Process</b></p><div class="figure-contents"><div class="mediaobject" align="center"><img src="images/schema_reading.png" align="middle" alt="schema reading"></div></div></div><br class="figure-break"><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>You should understand the difference between a writer schema (the application that wrote the message) and a reader schema (the receiving application).
We suggest taking a moment to read <a class="link" href="https://avro.apache.org/docs/1.7.6/spec.html" target="_top">the Avro terminology</a> and understand the process.
Spring Cloud Stream always fetches the writer schema to determine how to read a message.
If you want to get Avro&#8217;s schema evolution support working, you need to make sure that a <code class="literal">readerSchema</code> was properly set for your application.</p></td></tr></table></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_inter_application_communication" href="#_inter_application_communication"></a>33.&nbsp;Inter-Application Communication</h2></div></div></div><p>Spring Cloud Stream enables communication between applications. Inter-application communication is a complex issue spanning several concerns, as described in the following topics:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">&#8220;<a class="xref" href="#spring-cloud-stream-overview-connecting-multiple-application-instances" title="33.1&nbsp;Connecting Multiple Application Instances">Section&nbsp;33.1, &#8220;Connecting Multiple Application Instances&#8221;</a>&#8221;</li><li class="listitem">&#8220;<a class="xref" href="#spring-cloud-stream-overview-instance-index-instance-count" title="33.2&nbsp;Instance Index and Instance Count">Section&nbsp;33.2, &#8220;Instance Index and Instance Count&#8221;</a>&#8221;</li><li class="listitem">&#8220;<a class="xref" href="#spring-cloud-stream-overview-partitioning" title="33.3&nbsp;Partitioning">Section&nbsp;33.3, &#8220;Partitioning&#8221;</a>&#8221;</li></ul></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-connecting-multiple-application-instances" href="#spring-cloud-stream-overview-connecting-multiple-application-instances"></a>33.1&nbsp;Connecting Multiple Application Instances</h2></div></div></div><p>While Spring Cloud Stream makes it easy for individual Spring Boot applications to connect to messaging systems, the typical scenario for Spring Cloud Stream is the creation of multi-application pipelines, where microservice applications send data to each other.
You can achieve this scenario by correlating the input and output destinations of &#8220;adjacent&#8221; applications.</p><p>Suppose a design calls for the Time Source application to send data to the Log Sink application. You could use a common destination named <code class="literal">ticktock</code> for bindings within both applications.</p><p>Time Source (that has the channel name <code class="literal">output</code>) would set the following property:</p><pre class="screen">spring.cloud.stream.bindings.output.destination=ticktock</pre><p>Log Sink (that has the channel name <code class="literal">input</code>) would set the following property:</p><pre class="screen">spring.cloud.stream.bindings.input.destination=ticktock</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-instance-index-instance-count" href="#spring-cloud-stream-overview-instance-index-instance-count"></a>33.2&nbsp;Instance Index and Instance Count</h2></div></div></div><p>When scaling up Spring Cloud Stream applications, each instance can receive information about how many other instances of the same application exist and what its own instance index is.
Spring Cloud Stream does this through the <code class="literal">spring.cloud.stream.instanceCount</code> and <code class="literal">spring.cloud.stream.instanceIndex</code> properties.
For example, if there are three instances of a HDFS sink application, all three instances have <code class="literal">spring.cloud.stream.instanceCount</code> set to <code class="literal">3</code>, and the individual applications have <code class="literal">spring.cloud.stream.instanceIndex</code> set to <code class="literal">0</code>, <code class="literal">1</code>, and <code class="literal">2</code>, respectively.</p><p>When Spring Cloud Stream applications are deployed through Spring Cloud Data Flow, these properties are configured automatically; when Spring Cloud Stream applications are launched independently, these properties must be set correctly.
By default, <code class="literal">spring.cloud.stream.instanceCount</code> is <code class="literal">1</code>, and <code class="literal">spring.cloud.stream.instanceIndex</code> is <code class="literal">0</code>.</p><p>In a scaled-up scenario, correct configuration of these two properties is important for addressing partitioning behavior (see below) in general, and the two properties are always required by certain binders (for example, the Kafka binder) in order to ensure that data are split correctly across multiple consumer instances.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-stream-overview-partitioning" href="#spring-cloud-stream-overview-partitioning"></a>33.3&nbsp;Partitioning</h2></div></div></div><p>Partitioning in Spring Cloud Stream consists of two tasks:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">&#8220;<a class="xref" href="#spring-cloud-stream-overview-configuring-output-bindings-partitioning" title="33.3.1&nbsp;Configuring Output Bindings for Partitioning">Section&nbsp;33.3.1, &#8220;Configuring Output Bindings for Partitioning&#8221;</a>&#8221;</li><li class="listitem">&#8220;<a class="xref" href="#spring-cloud-stream-overview-configuring-input-bindings-partitioning" title="33.3.2&nbsp;Configuring Input Bindings for Partitioning">Section&nbsp;33.3.2, &#8220;Configuring Input Bindings for Partitioning&#8221;</a>&#8221;</li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-stream-overview-configuring-output-bindings-partitioning" href="#spring-cloud-stream-overview-configuring-output-bindings-partitioning"></a>33.3.1&nbsp;Configuring Output Bindings for Partitioning</h3></div></div></div><p>You can configure an output binding to send partitioned data by setting one and only one of its <code class="literal">partitionKeyExpression</code> or <code class="literal">partitionKeyExtractorName</code> properties, as well as its <code class="literal">partitionCount</code> property.</p><p>For example, the following is a valid and typical configuration:</p><pre class="screen">spring.cloud.stream.bindings.output.producer.partitionKeyExpression=payload.id
spring.cloud.stream.bindings.output.producer.partitionCount=5</pre><p>Based on that example configuration, data is sent to the target partition by using the following logic.</p><p>A partition key&#8217;s value is calculated for each message sent to a partitioned output channel based on the <code class="literal">partitionKeyExpression</code>.
The <code class="literal">partitionKeyExpression</code> is a SpEL expression that is evaluated against the outbound message for extracting the partitioning key.</p><p>If a SpEL expression is not sufficient for your needs, you can instead calculate the partition key value by providing an implementation of <code class="literal">org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy</code> and configuring it as a bean (by using the <code class="literal">@Bean</code> annotation).
If you have more then one bean of type <code class="literal">org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy</code> available in the Application Context, you can further filter it by specifying its name with the <code class="literal">partitionKeyExtractorName</code> property, as shown in the following example:</p><pre class="screen">--spring.cloud.stream.bindings.output.producer.partitionKeyExtractorName=customPartitionKeyExtractor
--spring.cloud.stream.bindings.output.producer.partitionCount=5
. . .
@Bean
public CustomPartitionKeyExtractorClass customPartitionKeyExtractor() {
return new CustomPartitionKeyExtractorClass();
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>In previous versions of Spring Cloud Stream, you could specify the implementation of <code class="literal">org.springframework.cloud.stream.binder.PartitionKeyExtractorStrategy</code> by setting the <code class="literal">spring.cloud.stream.bindings.output.producer.partitionKeyExtractorClass</code> property.
Since version 2.0, this property is deprecated, and support for it will be removed in a future version.</p></td></tr></table></div><p>Once the message key is calculated, the partition selection process determines the target partition as a value between <code class="literal">0</code> and <code class="literal">partitionCount - 1</code>.
The default calculation, applicable in most scenarios, is based on the following formula: <code class="literal">key.hashCode() % partitionCount</code>.
This can be customized on the binding, either by setting a SpEL expression to be evaluated against the 'key' (through the <code class="literal">partitionSelectorExpression</code> property) or by configuring an implementation of <code class="literal">org.springframework.cloud.stream.binder.PartitionSelectorStrategy</code> as a bean (by using the @Bean annotation).
Similar to the <code class="literal">PartitionKeyExtractorStrategy</code>, you can further filter it by using the <code class="literal">spring.cloud.stream.bindings.output.producer.partitionSelectorName</code> property when more than one bean of this type is available in the Application Context, as shown in the following example:</p><pre class="screen">--spring.cloud.stream.bindings.output.producer.partitionSelectorName=customPartitionSelector
. . .
@Bean
public CustomPartitionSelectorClass customPartitionSelector() {
return new CustomPartitionSelectorClass();
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>In previous versions of Spring Cloud Stream you could specify the implementation of <code class="literal">org.springframework.cloud.stream.binder.PartitionSelectorStrategy</code> by setting the <code class="literal">spring.cloud.stream.bindings.output.producer.partitionSelectorClass</code> property.
Since version 2.0, this property is deprecated and support for it will be removed in a future version.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-stream-overview-configuring-input-bindings-partitioning" href="#spring-cloud-stream-overview-configuring-input-bindings-partitioning"></a>33.3.2&nbsp;Configuring Input Bindings for Partitioning</h3></div></div></div><p>An input binding (with the channel name <code class="literal">input</code>) is configured to receive partitioned data by setting its <code class="literal">partitioned</code> property, as well as the <code class="literal">instanceIndex</code> and <code class="literal">instanceCount</code> properties on the application itself, as shown in the following example:</p><pre class="screen">spring.cloud.stream.bindings.input.consumer.partitioned=true
spring.cloud.stream.instanceIndex=3
spring.cloud.stream.instanceCount=5</pre><p>The <code class="literal">instanceCount</code> value represents the total number of application instances between which the data should be partitioned.
The <code class="literal">instanceIndex</code> must be a unique value across the multiple instances, with a value between <code class="literal">0</code> and <code class="literal">instanceCount - 1</code>.
The instance index helps each application instance to identify the unique partition(s) from which it receives data.
It is required by binders using technology that does not support partitioning natively.
For example, with RabbitMQ, there is a queue for each partition, with the queue name containing the instance index.
With Kafka, if <code class="literal">autoRebalanceEnabled</code> is <code class="literal">true</code> (default), Kafka takes care of distributing partitions across instances, and these properties are not required.
If <code class="literal">autoRebalanceEnabled</code> is set to false, the <code class="literal">instanceCount</code> and <code class="literal">instanceIndex</code> are used by the binder to determine which partition(s) the instance subscribes to (you must have at least as many partitions as there are instances).
The binder allocates the partitions instead of Kafka.
This might be useful if you want messages for a particular partition to always go to the same instance.
When a binder configuration requires them, it is important to set both values correctly in order to ensure that all of the data is consumed and that the application instances receive mutually exclusive datasets.</p><p>While a scenario in which using multiple instances for partitioned data processing may be complex to set up in a standalone case, Spring Cloud Dataflow can simplify the process significantly by populating both the input and output values correctly and by letting you rely on the runtime infrastructure to provide information about the instance index and instance count.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_testing" href="#_testing"></a>34.&nbsp;Testing</h2></div></div></div><p>Spring Cloud Stream provides support for testing your microservice applications without connecting to a messaging system.
You can do that by using the <code class="literal">TestSupportBinder</code> provided by the <code class="literal">spring-cloud-stream-test-support</code> library, which can be added as a test dependency to the application, as shown in the following example:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-test-support<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">TestSupportBinder</code> uses the Spring Boot autoconfiguration mechanism to supersede the other binders found on the classpath.
Therefore, when adding a binder as a dependency, you must make sure that the <code class="literal">test</code> scope is being used.</p></td></tr></table></div><p>The <code class="literal">TestSupportBinder</code> lets you interact with the bound channels and inspect any messages sent and received by the application.</p><p>For outbound message channels, the <code class="literal">TestSupportBinder</code> registers a single subscriber and retains the messages emitted by the application in a <code class="literal">MessageCollector</code>.
They can be retrieved during tests and have assertions made against them.</p><p>You can also send messages to inbound message channels so that the consumer application can consume the messages.
The following example shows how to test both input and output channels on a processor:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment= SpringBootTest.WebEnvironment.RANDOM_PORT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ExampleTest {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Processor processor;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MessageCollector messageCollector;
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<em><span class="hl-annotation" style="color: gray">@SuppressWarnings("unchecked")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testWiring() {
Message&lt;String&gt; message = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> GenericMessage&lt;&gt;(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"hello"</span>);
processor.input().send(message);
Message&lt;String&gt; received = (Message&lt;String&gt;) messageCollector.forChannel(processor.output()).poll();
assertThat(received.getPayload(), equalTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"hello world"</span>));
}
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyProcessor {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Processor channels;
<em><span class="hl-annotation" style="color: gray">@Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String transform(String in) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> in + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" world"</span>;
}
}
}</pre><p>In the preceding example, we create an application that has an input channel and an output channel, both bound through the <code class="literal">Processor</code> interface.
The bound interface is injected into the test so that we can have access to both channels.
We send a message on the input channel, and we use the <code class="literal">MessageCollector</code> provided by Spring Cloud Stream&#8217;s test support to capture that the message has been sent to the output channel as a result.
Once we have received the message, we can validate that the component functions correctly.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_disabling_the_test_binder_autoconfiguration" href="#_disabling_the_test_binder_autoconfiguration"></a>34.1&nbsp;Disabling the Test Binder Autoconfiguration</h2></div></div></div><p>The intent behind the test binder superseding all the other binders on the classpath is to make it easy to test your applications without making changes to your production dependencies.
In some cases (for example, integration tests) it is useful to use the actual production binders instead, and that requires disabling the test binder autoconfiguration.
To do so, you can exclude the <code class="literal">org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration</code> class by using one of the Spring Boot autoconfiguration exclusion mechanisms, as shown in the following example:</p><pre class="programlisting"> <em><span class="hl-annotation" style="color: gray">@SpringBootApplication(exclude = TestSupportBinderAutoConfiguration.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Processor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyProcessor {
<em><span class="hl-annotation" style="color: gray">@Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String transform(String in) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> in + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" world"</span>;
}
}</pre><p>When autoconfiguration is disabled, the test binder is available on the classpath, and its <code class="literal">defaultCandidate</code> property is set to <code class="literal">false</code> so that it does not interfere with the regular user configuration. It can be referenced under the name, <code class="literal">test</code>, as shown in the following example:</p><p><code class="literal">spring.cloud.stream.defaultBinder=test</code></p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_health_indicator_5" href="#_health_indicator_5"></a>35.&nbsp;Health Indicator</h2></div></div></div><p>Spring Cloud Stream provides a health indicator for binders.
It is registered under the name <code class="literal">binders</code> and can be enabled or disabled by setting the <code class="literal">management.health.binders.enabled</code> property.</p><p>By default <code class="literal">management.health.binders.enabled</code> is set to <code class="literal">false</code>.
Setting <code class="literal">management.health.binders.enabled</code> to <code class="literal">true</code> enables the health indicator, allowing you to access the <code class="literal">/health</code> endpoint to retrieve the binder health indicators.</p><p>Health indicators are binder-specific and certain binder implementations may not necessarily provide a health indicator.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-stream-overview-metrics-emitter" href="#spring-cloud-stream-overview-metrics-emitter"></a>36.&nbsp;Metrics Emitter</h2></div></div></div><p>Spring Boot Actuator provides dependency management and auto-configuration for <a class="link" href="https://micrometer.io/" target="_top">Micrometer</a>, an application metrics
facade that supports numerous <a class="link" href="https://docs.spring.io/spring-boot/docs/2.0.0.RELEASE/reference/htmlsingle/#production-ready-metrics" target="_top">monitoring systems</a>.</p><p>Spring Cloud Stream provides support for emitting any available micrometer-based metrics to a binding destination, allowing for periodic
collection of metric data from stream applications without relying on polling individual endpoints.</p><p>Metrics Emitter is activated by defining the <code class="literal">spring.cloud.stream.bindings.applicationMetrics.destination</code> property,
which specifies the name of the binding destination used by the current binder to publish metric messages.</p><p>For example:</p><pre class="programlisting">spring.cloud.stream.bindings.applicationMetrics.destination=myMetricDestination</pre><p>The preceding example instructs the binder to bind to <code class="literal">myMetricDestination</code> (that is, Rabbit exchange, Kafka topic, and others).</p><p>The following properties can be used for customizing the emission of metrics:</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.metrics.key</span></dt><dd><p class="simpara">The name of the metric being emitted. Should be a unique value per application.</p><p class="simpara">Default: <code class="literal">${spring.application.name:${vcap.application.name:${spring.config.name:application}}}</code></p></dd><dt><span class="term">spring.cloud.stream.metrics.properties</span></dt><dd><p class="simpara">Allows white listing application properties that are added to the metrics payload</p><p class="simpara">Default: null.</p></dd><dt><span class="term">spring.cloud.stream.metrics.meter-filter</span></dt><dd><p class="simpara">Pattern to control the 'meters' one wants to capture.
For example, specifying <code class="literal">spring.integration.*</code> captures metric information for meters whose name starts with <code class="literal">spring.integration.</code></p><p class="simpara">Default: all 'meters' are captured.</p></dd><dt><span class="term">spring.cloud.stream.metrics.schedule-interval</span></dt><dd><p class="simpara">Interval to control the rate of publishing metric data.</p><p class="simpara">Default: 1 min</p></dd></dl></div><p>Consider the following:</p><pre class="programlisting">java -jar time-source.jar \
--spring.cloud.stream.bindings.applicationMetrics.destination=someMetrics \
--spring.cloud.stream.metrics.properties=spring.application** \
--spring.cloud.stream.metrics.meter-filter=spring.integration.*</pre><p>The following example shows the payload of the data published to the binding destination as a result of the preceding command:</p><pre class="programlisting">{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"createdTime"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2018-03-23T14:48:12.700Z"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"properties"</span>: {
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"metrics"</span>: [
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spring.integration.send"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"tags"</span>: [
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"key"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"exception"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"value"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"none"</span>
},
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"key"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"value"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"input"</span>
},
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"key"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"result"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"value"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"success"</span>
},
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"key"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"type"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"value"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"channel"</span>
}
],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"TIMER"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"description"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Send processing time"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"baseUnit"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"milliseconds"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"timestamp"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2018-03-23T14:48:12.697Z"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sum"</span>: <span class="hl-number">130.340546</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"count"</span>: <span class="hl-number">6</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"mean"</span>: <span class="hl-number">21.72342433333333</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"upper"</span>: <span class="hl-number">116.176299</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"total"</span>: <span class="hl-number">130.340546</span>
}
]
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Given that the format of the Metric message has slightly changed after migrating to Micrometer, the published message will also have
a <code class="literal">STREAM_CLOUD_STREAM_VERSION</code> header set to <code class="literal">2.x</code> to help distinguish between Metric messages from the older versions of the Spring Cloud Stream.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_samples" href="#_samples"></a>37.&nbsp;Samples</h2></div></div></div><p>For Spring Cloud Stream samples, see the <a class="link" href="https://github.com/spring-cloud/spring-cloud-stream-samples" target="_top">spring-cloud-stream-samples</a> repository on GitHub.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_deploying_stream_applications_on_cloudfoundry" href="#_deploying_stream_applications_on_cloudfoundry"></a>37.1&nbsp;Deploying Stream Applications on CloudFoundry</h2></div></div></div><p>On CloudFoundry, services are usually exposed through a special environment variable called <a class="link" href="https://docs.cloudfoundry.org/devguide/deploy-apps/environment-variable.html#VCAP-SERVICES" target="_top">VCAP_SERVICES</a>.</p><p>When configuring your binder connections, you can use the values from an environment variable as explained on the <a class="link" href="http://docs.spring.io/spring-cloud-dataflow-server-cloudfoundry/docs/current-SNAPSHOT/reference/htmlsingle/#getting-started-ups" target="_top">dataflow Cloud Foundry Server</a> docs.</p></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_binder_implementations" href="#_binder_implementations"></a>Part&nbsp;VI.&nbsp;Binder Implementations</h1></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_apache_kafka_binder" href="#_apache_kafka_binder"></a>38.&nbsp;Apache Kafka Binder</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_usage" href="#_usage"></a>38.1&nbsp;Usage</h2></div></div></div><p>To use Apache Kafka binder, you need to add <code class="literal">spring-cloud-stream-binder-kafka</code> as a dependency to your Spring Cloud Stream application, as shown in the following example for Maven:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-binder-kafka<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>Alternatively, you can also use the Spring Cloud Stream Kafka Starter, as shown inn the following example for Maven:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-stream-kafka<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_apache_kafka_binder_overview" href="#_apache_kafka_binder_overview"></a>38.2&nbsp;Apache Kafka Binder Overview</h2></div></div></div><p>The following image shows a simplified diagram of how the Apache Kafka binder operates:</p><div class="figure"><a name="d0e11659" href="#d0e11659"></a><p class="title"><b>Figure&nbsp;38.1.&nbsp;Kafka Binder</b></p><div class="figure-contents"><div class="mediaobject"><img src="images/images/kafka-binder.png" alt="kafka binder"></div></div></div><br class="figure-break"><p>The Apache Kafka Binder implementation maps each destination to an Apache Kafka topic.
The consumer group maps directly to the same Apache Kafka concept.
Partitioning also maps directly to Apache Kafka partitions as well.</p><p>The binder currently uses the Apache Kafka <code class="literal">kafka-clients</code> 1.0.0 jar and is designed to be used with a broker of at least that version.
This client can communicate with older brokers (see the Kafka documentation), but certain features may not be available.
For example, with versions earlier than 0.11.x.x, native headers are not supported.
Also, 0.11.x.x does not support the <code class="literal">autoAddPartitions</code> property.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_configuration_options_2" href="#_configuration_options_2"></a>38.3&nbsp;Configuration Options</h2></div></div></div><p>This section contains the configuration options used by the Apache Kafka binder.</p><p>For common configuration options and properties pertaining to binder, see the <a class="link" href="#binding-properties" title="30.2&nbsp;Binding Properties">core documentation</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_kafka_binder_properties" href="#_kafka_binder_properties"></a>38.3.1&nbsp;Kafka Binder Properties</h3></div></div></div><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.kafka.binder.brokers</span></dt><dd><p class="simpara">A list of brokers to which the Kafka binder connects.</p><p class="simpara">Default: <code class="literal">localhost</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.defaultBrokerPort</span></dt><dd><p class="simpara"><code class="literal">brokers</code> allows hosts specified with or without port information (for example, <code class="literal">host1,host2:port2</code>).
This sets the default port when no port is configured in the broker list.</p><p class="simpara">Default: <code class="literal">9092</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.configuration</span></dt><dd><p class="simpara">Key/Value map of client properties (both producers and consumer) passed to all clients created by the binder.
Due to the fact that these properties are used by both producers and consumers, usage should be restricted to common properties&#8201;&#8212;&#8201;for example, security settings.
Properties here supersede any properties set in boot.</p><p class="simpara">Default: Empty map.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.consumerProperties</span></dt><dd><p class="simpara">Key/Value map of arbitrary Kafka client consumer properties.
Properties here supersede any properties set in boot and in the <code class="literal">configuration</code> property above.</p><p class="simpara">Default: Empty map.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.headers</span></dt><dd><p class="simpara">The list of custom headers that are transported by the binder.
Only required when communicating with older applications (&#8656; 1.3.x) with a <code class="literal">kafka-clients</code> version &lt; 0.11.0.0. Newer versions support headers natively.</p><p class="simpara">Default: empty.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.healthTimeout</span></dt><dd><p class="simpara">The time to wait to get partition information, in seconds.
Health reports as down if this timer expires.</p><p class="simpara">Default: 10.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.requiredAcks</span></dt><dd><p class="simpara">The number of required acks on the broker.
See the Kafka documentation for the producer <code class="literal">acks</code> property.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.minPartitionCount</span></dt><dd><p class="simpara">Effective only if <code class="literal">autoCreateTopics</code> or <code class="literal">autoAddPartitions</code> is set.
The global minimum number of partitions that the binder configures on topics on which it produces or consumes data.
It can be superseded by the <code class="literal">partitionCount</code> setting of the producer or by the value of <code class="literal">instanceCount * concurrency</code> settings of the producer (if either is larger).</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.producerProperties</span></dt><dd><p class="simpara">Key/Value map of arbitrary Kafka client producer properties.
Properties here supersede any properties set in boot and in the <code class="literal">configuration</code> property above.</p><p class="simpara">Default: Empty map.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.replicationFactor</span></dt><dd><p class="simpara">The replication factor of auto-created topics if <code class="literal">autoCreateTopics</code> is active.
Can be overridden on each binding.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.autoCreateTopics</span></dt><dd><p class="simpara">If set to <code class="literal">true</code>, the binder creates new topics automatically.
If set to <code class="literal">false</code>, the binder relies on the topics being already configured.
In the latter case, if the topics do not exist, the binder fails to start.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>This setting is independent of the <code class="literal">auto.topic.create.enable</code> setting of the broker and does not influence it.
If the server is set to auto-create topics, they may be created as part of the metadata retrieval request, with default broker settings.</p></td></tr></table></div><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.autoAddPartitions</span></dt><dd><p class="simpara">If set to <code class="literal">true</code>, the binder creates new partitions if required.
If set to <code class="literal">false</code>, the binder relies on the partition size of the topic being already configured.
If the partition count of the target topic is smaller than the expected value, the binder fails to start.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix</span></dt><dd><p class="simpara">Enables transactions in the binder. See <code class="literal">transaction.id</code> in the Kafka documentation and <a class="link" href="https://docs.spring.io/spring-kafka/reference/html/_reference.html#transactions" target="_top">Transactions</a> in the <code class="literal">spring-kafka</code> documentation.
When transactions are enabled, individual <code class="literal">producer</code> properties are ignored and all producers use the <code class="literal">spring.cloud.stream.kafka.binder.transaction.producer.*</code> properties.</p><p class="simpara">Default <code class="literal">null</code> (no transactions)</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.transaction.producer.*</span></dt><dd><p class="simpara">Global producer properties for producers in a transactional binder.
See <code class="literal">spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix</code> and <a class="xref" href="#kafka-producer-properties" title="38.3.3&nbsp;Kafka Producer Properties">Section&nbsp;38.3.3, &#8220;Kafka Producer Properties&#8221;</a> and the general producer properties supported by all binders.</p><p class="simpara">Default: See individual producer properties.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.headerMapperBeanName</span></dt><dd><p class="simpara">The bean name of a <code class="literal">KafkaHeaderMapper</code> used for mapping <code class="literal">spring-messaging</code> headers to and from Kafka headers.
Use this, for example, if you wish to customize the trusted packages in a <code class="literal">DefaultKafkaHeaderMapper</code> that uses JSON deserialization for the headers.</p><p class="simpara">Default: none.</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="kafka-consumer-properties" href="#kafka-consumer-properties"></a>38.3.2&nbsp;Kafka Consumer Properties</h3></div></div></div><p>The following properties are available for Kafka consumers only and
must be prefixed with <code class="literal">spring.cloud.stream.kafka.bindings.&lt;channelName&gt;.consumer.</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">admin.configuration</span></dt><dd><p class="simpara">A <code class="literal">Map</code> of Kafka topic properties used when provisioning topics&#8201;&#8212;&#8201;for example, <code class="literal">spring.cloud.stream.kafka.bindings.input.consumer.admin.configuration.message.format.version=0.9.0.0</code></p><p class="simpara">Default: none.</p></dd><dt><span class="term">admin.replicas-assignment</span></dt><dd><p class="simpara">A Map&lt;Integer, List&lt;Integer&gt;&gt; of replica assignments, with the key being the partition and the value being the assignments.
Used when provisioning new topics.
See the <code class="literal">NewTopic</code> Javadocs in the <code class="literal">kafka-clients</code> jar.</p><p class="simpara">Default: none.</p></dd><dt><span class="term">admin.replication-factor</span></dt><dd><p class="simpara">The replication factor to use when provisioning topics. Overrides the binder-wide setting.
Ignored if <code class="literal">replicas-assignments</code> is present.</p><p class="simpara">Default: none (the binder-wide default of 1 is used).</p></dd><dt><span class="term">autoRebalanceEnabled</span></dt><dd><p class="simpara">When <code class="literal">true</code>, topic partitions is automatically rebalanced between the members of a consumer group.
When <code class="literal">false</code>, each consumer is assigned a fixed set of partitions based on <code class="literal">spring.cloud.stream.instanceCount</code> and <code class="literal">spring.cloud.stream.instanceIndex</code>.
This requires both the <code class="literal">spring.cloud.stream.instanceCount</code> and <code class="literal">spring.cloud.stream.instanceIndex</code> properties to be set appropriately on each launched instance.
The value of the <code class="literal">spring.cloud.stream.instanceCount</code> property must typically be greater than 1 in this case.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">ackEachRecord</span></dt><dd><p class="simpara">When <code class="literal">autoCommitOffset</code> is <code class="literal">true</code>, this setting dictates whether to commit the offset after each record is processed.
By default, offsets are committed after all records in the batch of records returned by <code class="literal">consumer.poll()</code> have been processed.
The number of records returned by a poll can be controlled with the <code class="literal">max.poll.records</code> Kafka property, which is set through the consumer <code class="literal">configuration</code> property.
Setting this to <code class="literal">true</code> may cause a degradation in performance, but doing so reduces the likelihood of redelivered records when a failure occurs.
Also, see the binder <code class="literal">requiredAcks</code> property, which also affects the performance of committing offsets.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">autoCommitOffset</span></dt><dd><p class="simpara">Whether to autocommit offsets when a message has been processed.
If set to <code class="literal">false</code>, a header with the key <code class="literal">kafka_acknowledgment</code> of the type <code class="literal">org.springframework.kafka.support.Acknowledgment</code> header is present in the inbound message.
Applications may use this header for acknowledging messages.
See the examples section for details.
When this property is set to <code class="literal">false</code>, Kafka binder sets the ack mode to <code class="literal">org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMode.MANUAL</code> and the application is responsible for acknowledging records.
Also see <code class="literal">ackEachRecord</code>.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">autoCommitOnError</span></dt><dd><p class="simpara">Effective only if <code class="literal">autoCommitOffset</code> is set to <code class="literal">true</code>.
If set to <code class="literal">false</code>, it suppresses auto-commits for messages that result in errors and commits only for successful messages. It allows a stream to automatically replay from the last successfully processed message, in case of persistent failures.
If set to <code class="literal">true</code>, it always auto-commits (if auto-commit is enabled).
If not set (the default), it effectively has the same value as <code class="literal">enableDlq</code>, auto-committing erroneous messages if they are sent to a DLQ and not committing them otherwise.</p><p class="simpara">Default: not set.</p></dd><dt><span class="term">resetOffsets</span></dt><dd><p class="simpara">Whether to reset offsets on the consumer to the value provided by startOffset.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">startOffset</span></dt><dd><p class="simpara">The starting offset for new groups.
Allowed values: <code class="literal">earliest</code> and <code class="literal">latest</code>.
If the consumer group is set explicitly for the consumer 'binding' (through <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.group</code>), 'startOffset' is set to <code class="literal">earliest</code>. Otherwise, it is set to <code class="literal">latest</code> for the <code class="literal">anonymous</code> consumer group.
Also see <code class="literal">resetOffsets</code> (earlier in this list).</p><p class="simpara">Default: null (equivalent to <code class="literal">earliest</code>).</p></dd><dt><span class="term">enableDlq</span></dt><dd><p class="simpara">When set to true, it enables DLQ behavior for the consumer.
By default, messages that result in errors are forwarded to a topic named <code class="literal">error.&lt;destination&gt;.&lt;group&gt;</code>.
The DLQ topic name can be configurable by setting the <code class="literal">dlqName</code> property.
This provides an alternative option to the more common Kafka replay scenario for the case when the number of errors is relatively small and replaying the entire original topic may be too cumbersome.
See <a class="xref" href="#kafka-dlq-processing" title="38.6&nbsp;Dead-Letter Topic Processing">Section&nbsp;38.6, &#8220;Dead-Letter Topic Processing&#8221;</a> processing for more information.
Starting with version 2.0, messages sent to the DLQ topic are enhanced with the following headers: <code class="literal">x-original-topic</code>, <code class="literal">x-exception-message</code>, and <code class="literal">x-exception-stacktrace</code> as <code class="literal">byte[]</code>.
<span class="strong"><strong>Not allowed when <code class="literal">destinationIsPattern</code> is <code class="literal">true</code>.</strong></span></p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">configuration</span></dt><dd><p class="simpara">Map with a key/value pair containing generic Kafka consumer properties.</p><p class="simpara">Default: Empty map.</p></dd><dt><span class="term">dlqName</span></dt><dd><p class="simpara">The name of the DLQ topic to receive the error messages.</p><p class="simpara">Default: null (If not specified, messages that result in errors are forwarded to a topic named <code class="literal">error.&lt;destination&gt;.&lt;group&gt;</code>).</p></dd><dt><span class="term">dlqProducerProperties</span></dt><dd><p class="simpara">Using this, DLQ-specific producer properties can be set.
All the properties available through kafka producer properties can be set through this property.</p><p class="simpara">Default: Default Kafka producer properties.</p></dd><dt><span class="term">standardHeaders</span></dt><dd><p class="simpara">Indicates which standard headers are populated by the inbound channel adapter.
Allowed values: <code class="literal">none</code>, <code class="literal">id</code>, <code class="literal">timestamp</code>, or <code class="literal">both</code>.
Useful if using native deserialization and the first component to receive a message needs an <code class="literal">id</code> (such as an aggregator that is configured to use a JDBC message store).</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">converterBeanName</span></dt><dd><p class="simpara">The name of a bean that implements <code class="literal">RecordMessageConverter</code>. Used in the inbound channel adapter to replace the default <code class="literal">MessagingMessageConverter</code>.</p><p class="simpara">Default: <code class="literal">null</code></p></dd><dt><span class="term">idleEventInterval</span></dt><dd><p class="simpara">The interval, in milliseconds, between events indicating that no messages have recently been received.
Use an <code class="literal">ApplicationListener&lt;ListenerContainerIdleEvent&gt;</code> to receive these events.
See <a class="xref" href="#pause-resume" title="Example: Pausing and Resuming the Consumer">the section called &#8220;Example: Pausing and Resuming the Consumer&#8221;</a> for a usage example.</p><p class="simpara">Default: <code class="literal">30000</code></p></dd><dt><span class="term">destinationIsPattern</span></dt><dd><p class="simpara">When true, the destination is treated as a regular expression <code class="literal">Pattern</code> used to match topic names by the broker.
When true, topics are not provisioned, and <code class="literal">enableDlq</code> is not allowed, because the binder does not know the topic names during the provisioning phase.
Note, the time taken to detect new topics that match the pattern is controlled by the consumer property <code class="literal">metadata.max.age.ms</code>, which (at the time of writing) defaults to 300,000ms (5 minutes).
This can be configured using the <code class="literal">configuration</code> property above.</p><p class="simpara">Default: <code class="literal">false</code></p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="kafka-producer-properties" href="#kafka-producer-properties"></a>38.3.3&nbsp;Kafka Producer Properties</h3></div></div></div><p>The following properties are available for Kafka producers only and
must be prefixed with <code class="literal">spring.cloud.stream.kafka.bindings.&lt;channelName&gt;.producer.</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">admin.configuration</span></dt><dd><p class="simpara">A <code class="literal">Map</code> of Kafka topic properties used when provisioning new topics&#8201;&#8212;&#8201;for example, <code class="literal">spring.cloud.stream.kafka.bindings.input.consumer.admin.configuration.message.format.version=0.9.0.0</code></p><p class="simpara">Default: none.</p></dd><dt><span class="term">admin.replicas-assignment</span></dt><dd><p class="simpara">A Map&lt;Integer, List&lt;Integer&gt;&gt; of replica assignments, with the key being the partition and the value being the assignments.
Used when provisioning new topics.
See <code class="literal">NewTopic</code> javadocs in the <code class="literal">kafka-clients</code> jar.</p><p class="simpara">Default: none.</p></dd><dt><span class="term">admin.replication-factor</span></dt><dd><p class="simpara">The replication factor to use when provisioning new topics. Overrides the binder-wide setting.
Ignored if <code class="literal">replicas-assignments</code> is present.</p><p class="simpara">Default: none (the binder-wide default of 1 is used).</p></dd><dt><span class="term">bufferSize</span></dt><dd><p class="simpara">Upper limit, in bytes, of how much data the Kafka producer attempts to batch before sending.</p><p class="simpara">Default: <code class="literal">16384</code>.</p></dd><dt><span class="term">sync</span></dt><dd><p class="simpara">Whether the producer is synchronous.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">batchTimeout</span></dt><dd><p class="simpara">How long the producer waits to allow more messages to accumulate in the same batch before sending the messages.
(Normally, the producer does not wait at all and simply sends all the messages that accumulated while the previous send was in progress.) A non-zero value may increase throughput at the expense of latency.</p><p class="simpara">Default: <code class="literal">0</code>.</p></dd><dt><span class="term">messageKeyExpression</span></dt><dd><p class="simpara">A SpEL expression evaluated against the outgoing message used to populate the key of the produced Kafka message&#8201;&#8212;&#8201;for example, <code class="literal">headers['myKey']</code>.
The payload cannot be used because, by the time this expression is evaluated, the payload is already in the form of a <code class="literal">byte[]</code>.</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">headerPatterns</span></dt><dd><p class="simpara">A comma-delimited list of simple patterns to match Spring messaging headers to be mapped to the Kafka <code class="literal">Headers</code> in the <code class="literal">ProducerRecord</code>.
Patterns can begin or end with the wildcard character (asterisk).
Patterns can be negated by prefixing with <code class="literal">!</code>.
Matching stops after the first match (positive or negative).
For example <code class="literal">!ask,as*</code> will pass <code class="literal">ash</code> but not <code class="literal">ask</code>.
<code class="literal">id</code> and <code class="literal">timestamp</code> are never mapped.</p><p class="simpara">Default: <code class="literal">*</code> (all headers - except the <code class="literal">id</code> and <code class="literal">timestamp</code>)</p></dd><dt><span class="term">configuration</span></dt><dd><p class="simpara">Map with a key/value pair containing generic Kafka producer properties.</p><p class="simpara">Default: Empty map.</p></dd></dl></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The Kafka binder uses the <code class="literal">partitionCount</code> setting of the producer as a hint to create a topic with the given partition count (in conjunction with the <code class="literal">minPartitionCount</code>, the maximum of the two being the value being used).
Exercise caution when configuring both <code class="literal">minPartitionCount</code> for a binder and <code class="literal">partitionCount</code> for an application, as the larger value is used.
If a topic already exists with a smaller partition count and <code class="literal">autoAddPartitions</code> is disabled (the default), the binder fails to start.
If a topic already exists with a smaller partition count and <code class="literal">autoAddPartitions</code> is enabled, new partitions are added.
If a topic already exists with a larger number of partitions than the maximum of (<code class="literal">minPartitionCount</code> or <code class="literal">partitionCount</code>), the existing partition count is used.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_usage_examples" href="#_usage_examples"></a>38.3.4&nbsp;Usage examples</h3></div></div></div><p>In this section, we show the use of the preceding properties for specific scenarios.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_example_setting_literal_autocommitoffset_literal_to_literal_false_literal_and_relying_on_manual_acking" href="#_example_setting_literal_autocommitoffset_literal_to_literal_false_literal_and_relying_on_manual_acking"></a>Example: Setting <code class="literal">autoCommitOffset</code> to <code class="literal">false</code> and Relying on Manual Acking</h4></div></div></div><p>This example illustrates how one may manually acknowledge offsets in a consumer application.</p><p>This example requires that <code class="literal">spring.cloud.stream.kafka.bindings.input.consumer.autoCommitOffset</code> be set to <code class="literal">false</code>.
Use the corresponding input channel name for your example.</p><pre class="screen">@SpringBootApplication
@EnableBinding(Sink.class)
public class ManuallyAcknowdledgingConsumer {
public static void main(String[] args) {
SpringApplication.run(ManuallyAcknowdledgingConsumer.class, args);
}
@StreamListener(Sink.INPUT)
public void process(Message&lt;?&gt; message) {
Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
if (acknowledgment != null) {
System.out.println("Acknowledgment provided");
acknowledgment.acknowledge();
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_example_security_configuration" href="#_example_security_configuration"></a>Example: Security Configuration</h4></div></div></div><p>Apache Kafka 0.9 supports secure connections between client and brokers.
To take advantage of this feature, follow the guidelines in the <a class="link" href="http://kafka.apache.org/090/documentation.html#security_configclients" target="_top">Apache Kafka Documentation</a> as well as the Kafka 0.9 <a class="link" href="http://docs.confluent.io/2.0.0/kafka/security.html" target="_top">security guidelines from the Confluent documentation</a>.
Use the <code class="literal">spring.cloud.stream.kafka.binder.configuration</code> option to set security properties for all clients created by the binder.</p><p>For example, to set <code class="literal">security.protocol</code> to <code class="literal">SASL_SSL</code>, set the following property:</p><pre class="screen">spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_SSL</pre><p>All the other security properties can be set in a similar manner.</p><p>When using Kerberos, follow the instructions in the <a class="link" href="http://kafka.apache.org/090/documentation.html#security_sasl_clientconfig" target="_top">reference documentation</a> for creating and referencing the JAAS configuration.</p><p>Spring Cloud Stream supports passing JAAS configuration information to the application by using a JAAS configuration file and using Spring Boot properties.</p><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_using_jaas_configuration_files" href="#_using_jaas_configuration_files"></a>Using JAAS Configuration Files</h5></div></div></div><p>The JAAS and (optionally) krb5 file locations can be set for Spring Cloud Stream applications by using system properties.
The following example shows how to launch a Spring Cloud Stream application with SASL and Kerberos by using a JAAS configuration file:</p><pre class="programlisting"> java -Djava.security.auth.login.config=/path.to/kafka_client_jaas.conf -jar log.jar \
--spring.cloud.stream.kafka.binder.brokers=secure.server:<span class="hl-number">9092</span> \
--spring.cloud.stream.bindings.input.destination=stream.ticktock \
--spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_PLAINTEXT</pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_using_spring_boot_properties" href="#_using_spring_boot_properties"></a>Using Spring Boot Properties</h5></div></div></div><p>As an alternative to having a JAAS configuration file, Spring Cloud Stream provides a mechanism for setting up the JAAS configuration for Spring Cloud Stream applications by using Spring Boot properties.</p><p>The following properties can be used to configure the login context of the Kafka client:</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.kafka.binder.jaas.loginModule</span></dt><dd><p class="simpara">The login module name. Not necessary to be set in normal cases.</p><p class="simpara">Default: <code class="literal">com.sun.security.auth.module.Krb5LoginModule</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.jaas.controlFlag</span></dt><dd><p class="simpara">The control flag of the login module.</p><p class="simpara">Default: <code class="literal">required</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.jaas.options</span></dt><dd><p class="simpara">Map with a key/value pair containing the login module options.</p><p class="simpara">Default: Empty map.</p></dd></dl></div><p>The following example shows how to launch a Spring Cloud Stream application with SASL and Kerberos by using Spring Boot configuration properties:</p><pre class="programlisting"> java --spring.cloud.stream.kafka.binder.brokers=secure.server:<span class="hl-number">9092</span> \
--spring.cloud.stream.bindings.input.destination=stream.ticktock \
--spring.cloud.stream.kafka.binder.autoCreateTopics=false \
--spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_PLAINTEXT \
--spring.cloud.stream.kafka.binder.jaas.options.useKeyTab=true \
--spring.cloud.stream.kafka.binder.jaas.options.storeKey=true \
--spring.cloud.stream.kafka.binder.jaas.options.keyTab=/etc/security/keytabs/kafka_client.keytab \
--spring.cloud.stream.kafka.binder.jaas.options.principal=kafka-client-<span class="hl-number">1</span>@EXAMPLE.COM</pre><p>The preceding example represents the equivalent of the following JAAS file:</p><pre class="screen">KafkaClient {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="/etc/security/keytabs/kafka_client.keytab"
principal="kafka-client-1@EXAMPLE.COM";
};</pre><p>If the topics required already exist on the broker or will be created by an administrator, autocreation can be turned off and only client JAAS properties need to be sent.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Do not mix JAAS configuration files and Spring Boot properties in the same application.
If the <code class="literal">-Djava.security.auth.login.config</code> system property is already present, Spring Cloud Stream ignores the Spring Boot properties.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Be careful when using the <code class="literal">autoCreateTopics</code> and <code class="literal">autoAddPartitions</code> with Kerberos.
Usually, applications may use principals that do not have administrative rights in Kafka and Zookeeper.
Consequently, relying on Spring Cloud Stream to create/modify topics may fail.
In secure environments, we strongly recommend creating topics and managing ACLs administratively by using Kafka tooling.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="pause-resume" href="#pause-resume"></a>Example: Pausing and Resuming the Consumer</h4></div></div></div><p>If you wish to suspend consumption but not cause a partition rebalance, you can pause and resume the consumer.
This is facilitated by adding the <code class="literal">Consumer</code> as a parameter to your <code class="literal">@StreamListener</code>.
To resume, you need an <code class="literal">ApplicationListener</code> for <code class="literal">ListenerContainerIdleEvent</code> instances.
The frequency at which events are published is controlled by the <code class="literal">idleEventInterval</code> property.
Since the consumer is not thread-safe, you must call these methods on the calling thread.</p><p>The following simple application shows how to pause and resume:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> in(String in, <em><span class="hl-annotation" style="color: gray">@Header(KafkaHeaders.CONSUMER)</span></em> Consumer&lt;?, ?&gt; consumer) {
System.out.println(in);
consumer.pause(Collections.singleton(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TopicPartition(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myTopic"</span>, <span class="hl-number">0</span>)));
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ApplicationListener&lt;ListenerContainerIdleEvent&gt; idleListener() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> event -&gt; {
System.out.println(event);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (event.getConsumer().paused().size() &gt; <span class="hl-number">0</span>) {
event.getConsumer().resume(event.getConsumer().paused());
}
};
}
}</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="kafka-error-channels" href="#kafka-error-channels"></a>38.4&nbsp;Error Channels</h2></div></div></div><p>Starting with version 1.3, the binder unconditionally sends exceptions to an error channel for each consumer destination and can also be configured to send async producer send failures to an error channel.
See <a class="xref" href="#spring-cloud-stream-overview-error-handling" title="28.4&nbsp;Error Handling">Section&nbsp;28.4, &#8220;Error Handling&#8221;</a> for more information.</p><p>The payload of the <code class="literal">ErrorMessage</code> for a send failure is a <code class="literal">KafkaSendFailureException</code> with properties:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">failedMessage</code>: The Spring Messaging <code class="literal">Message&lt;?&gt;</code> that failed to be sent.</li><li class="listitem"><code class="literal">record</code>: The raw <code class="literal">ProducerRecord</code> that was created from the <code class="literal">failedMessage</code></li></ul></div><p>There is no automatic handling of producer exceptions (such as sending to a <a class="link" href="#kafka-dlq-processing" title="38.6&nbsp;Dead-Letter Topic Processing">Dead-Letter queue</a>).
You can consume these exceptions with your own Spring Integration flow.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="kafka-metrics" href="#kafka-metrics"></a>38.5&nbsp;Kafka Metrics</h2></div></div></div><p>Kafka binder module exposes the following metrics:</p><p><code class="literal">spring.cloud.stream.binder.kafka.offset</code>: This metric indicates how many messages have not been yet consumed from a given binder&#8217;s topic by a given consumer group.
The metrics provided are based on the Mircometer metrics library. The metric contains the consumer group information, topic and the actual lag in committed offset from the latest offset on the topic.
This metric is particularly useful for providing auto-scaling feedback to a PaaS platform.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="kafka-dlq-processing" href="#kafka-dlq-processing"></a>38.6&nbsp;Dead-Letter Topic Processing</h2></div></div></div><p>Because you cannot anticipate how users would want to dispose of dead-lettered messages, the framework does not provide any standard mechanism to handle them.
If the reason for the dead-lettering is transient, you may wish to route the messages back to the original topic.
However, if the problem is a permanent issue, that could cause an infinite loop.
The sample Spring Boot application within this topic is an example of how to route those messages back to the original topic, but it moves them to a &#8220;parking lot&#8221; topic after three attempts.
The application is another spring-cloud-stream application that reads from the dead-letter topic.
It terminates when no messages are received for 5 seconds.</p><p>The examples assume the original destination is <code class="literal">so8400out</code> and the consumer group is <code class="literal">so8400</code>.</p><p>There are a couple of strategies to consider:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Consider running the rerouting only when the main application is not running.
Otherwise, the retries for transient errors are used up very quickly.</li><li class="listitem">Alternatively, use a two-stage approach: Use this application to route to a third topic and another to route from there back to the main topic.</li></ul></div><p>The following code listings show the sample application:</p><p><b>application.properties.&nbsp;</b>
</p><pre class="screen">spring.cloud.stream.bindings.input.group=so8400replay
spring.cloud.stream.bindings.input.destination=error.so8400out.so8400
spring.cloud.stream.bindings.output.destination=so8400out
spring.cloud.stream.bindings.output.producer.partitioned=true
spring.cloud.stream.bindings.parkingLot.destination=so8400in.parkingLot
spring.cloud.stream.bindings.parkingLot.producer.partitioned=true
spring.cloud.stream.kafka.binder.configuration.auto.offset.reset=earliest
spring.cloud.stream.kafka.binder.headers=x-retries</pre><p>
</p><p><b>Application.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(TwoOutputProcessor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ReRouteDlqKApplication <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> CommandLineRunner {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_RETRIES_HEADER = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-retries"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(ReRouteDlqKApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args).close();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> AtomicInteger processed = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AtomicInteger();
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MessageChannel parkingLot;
<em><span class="hl-annotation" style="color: gray">@StreamListener(Processor.INPUT)</span></em>
<em><span class="hl-annotation" style="color: gray">@SendTo(Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Message&lt;?&gt; reRoute(Message&lt;?&gt; failed) {
processed.incrementAndGet();
Integer retries = failed.getHeaders().get(X_RETRIES_HEADER, Integer.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retries == null) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"First retry for "</span> + failed);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> MessageBuilder.fromMessage(failed)
.setHeader(X_RETRIES_HEADER, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Integer(<span class="hl-number">1</span>))
.setHeader(BinderHeaders.PARTITION_OVERRIDE,
failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
.build();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retries.intValue() &lt; <span class="hl-number">3</span>) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Another retry for "</span> + failed);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> MessageBuilder.fromMessage(failed)
.setHeader(X_RETRIES_HEADER, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Integer(retries.intValue() + <span class="hl-number">1</span>))
.setHeader(BinderHeaders.PARTITION_OVERRIDE,
failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
.build();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Retries exhausted for "</span> + failed);
parkingLot.send(MessageBuilder.fromMessage(failed)
.setHeader(BinderHeaders.PARTITION_OVERRIDE,
failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
.build());
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> run(String... args) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">while</span> (true) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> count = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.processed.get();
Thread.sleep(<span class="hl-number">5000</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (count == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.processed.get()) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Idle, terminating"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span>;
}
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TwoOutputProcessor <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Processor {
<em><span class="hl-annotation" style="color: gray">@Output("parkingLot")</span></em>
MessageChannel parkingLot();
}
}</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_partitioning_with_the_kafka_binder" href="#_partitioning_with_the_kafka_binder"></a>38.7&nbsp;Partitioning with the Kafka Binder</h2></div></div></div><p>Apache Kafka supports topic partitioning natively.</p><p>Sometimes it is advantageous to send data to specific partitions&#8201;&#8212;&#8201;for example, when you want to strictly order message processing (all messages for a particular customer should go to the same partition).</p><p>The following example shows how to configure the producer and consumer side:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> KafkaPartitionProducerApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Random RANDOM = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Random(System.currentTimeMillis());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String[] data = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> String[] {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux1"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo2"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux2"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo3"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar3"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux3"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo4"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar4"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux4"</span>,
};
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(KafkaPartitionProducerApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
.web(false)
.run(args);
}
<em><span class="hl-annotation" style="color: gray">@InboundChannelAdapter(channel = Source.OUTPUT, poller = @Poller(fixedRate = "5000"))</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Message&lt;?&gt; generate() {
String value = data[RANDOM.nextInt(data.length)];
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Sending: "</span> + value);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> MessageBuilder.withPayload(value)
.setHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"partitionKey"</span>, value)
.build();
}
}</pre><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stream</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bindings</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> output</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: partitioned.topic
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> producer</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partitioned</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partition-key-expression</span>: headers[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'partitionKey'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partition-count</span>: <span class="hl-number">12</span></pre><p>
</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The topic must be provisioned to have enough partitions to achieve the desired concurrency for all consumer groups.
The above configuration supports up to 12 consumer instances (6 if their <code class="literal">concurrency</code> is 2, 4 if their concurrency is 3, and so on).
It is generally best to &#8220;over-provision&#8221; the partitions to allow for future increases in consumers or concurrency.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The preceding configuration uses the default partitioning (<code class="literal">key.hashCode() % partitionCount</code>).
This may or may not provide a suitably balanced algorithm, depending on the key values.
You can override this default by using the <code class="literal">partitionSelectorExpression</code> or <code class="literal">partitionSelectorClass</code> properties.</p></td></tr></table></div><p>Since partitions are natively handled by Kafka, no special configuration is needed on the consumer side.
Kafka allocates partitions across the instances.</p><p>The following Spring Boot application listens to a Kafka stream and prints (to the console) the partition ID to which each message goes:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> KafkaPartitionConsumerApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(KafkaPartitionConsumerApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
.web(false)
.run(args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> listen(<em><span class="hl-annotation" style="color: gray">@Payload</span></em> String in, <em><span class="hl-annotation" style="color: gray">@Header(KafkaHeaders.RECEIVED_PARTITION_ID)</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> partition) {
System.out.println(in + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" received from partition "</span> + partition);
}
}</pre><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stream</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bindings</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> input</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: partitioned.topic
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> group</span>: myGroup</pre><p>
</p><p>You can add instances as needed.
Kafka rebalances the partition allocations.
If the instance count (or <code class="literal">instance count * concurrency</code>) exceeds the number of partitions, some consumers are idle.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_apache_kafka_streams_binder" href="#_apache_kafka_streams_binder"></a>39.&nbsp;Apache Kafka Streams Binder</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_usage_2" href="#_usage_2"></a>39.1&nbsp;Usage</h2></div></div></div><p>For using the Kafka Streams binder, you just need to add it to your Spring Cloud Stream application, using the following
Maven coordinates:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-binder-kafka-streams<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_kafka_streams_binder_overview" href="#_kafka_streams_binder_overview"></a>39.2&nbsp;Kafka Streams Binder Overview</h2></div></div></div><p>Spring Cloud Stream&#8217;s Apache Kafka support also includes a binder implementation designed explicitly for Apache Kafka
Streams binding. With this native integration, a Spring Cloud Stream "processor" application can directly use the
<a class="link" href="https://kafka.apache.org/documentation/streams/developer-guide" target="_top">Apache Kafka Streams</a> APIs in the core business logic.</p><p>Kafka Streams binder implementation builds on the foundation provided by the <a class="link" href="http://docs.spring.io/spring-kafka/reference/html/_reference.html#kafka-streams" target="_top">Kafka Streams in Spring Kafka</a>
project.</p><p>Kafka Streams binder provides binding capabilities for the three major types in Kafka Streams - KStream, KTable and GlobalKTable.</p><p>As part of this native integration, the high-level <a class="link" href="https://docs.confluent.io/current/streams/developer-guide/dsl-api.html" target="_top">Streams DSL</a>
provided by the Kafka Streams API is available for use in the business logic.</p><p>An early version of the <a class="link" href="https://docs.confluent.io/current/streams/developer-guide/processor-api.html" target="_top">Processor API</a>
support is available as well.</p><p>As noted early-on, Kafka Streams support in Spring Cloud Stream is strictly only available for use in the Processor model.
A model in which the messages read from an inbound topic, business processing can be applied, and the transformed messages
can be written to an outbound topic. It can also be used in Processor applications with a no-outbound destination.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_streams_dsl" href="#_streams_dsl"></a>39.2.1&nbsp;Streams DSL</h3></div></div></div><p>This application consumes data from a Kafka topic (e.g., <code class="literal">words</code>), computes word count for each unique word in a 5 seconds
time window, and the computed results are sent to a downstream topic (e.g., <code class="literal">counts</code>) for further processing.</p><pre class="screen">@SpringBootApplication
@EnableBinding(KStreamProcessor.class)
public class WordCountProcessorApplication {
@StreamListener("input")
@SendTo("output")
public KStream&lt;?, WordCount&gt; process(KStream&lt;?, String&gt; input) {
return input
.flatMapValues(value -&gt; Arrays.asList(value.toLowerCase().split("\\W+")))
.groupBy((key, value) -&gt; value)
.windowedBy(TimeWindows.of(5000))
.count(Materialized.as("WordCounts-multi"))
.toStream()
.map((key, value) -&gt; new KeyValue&lt;&gt;(null, new WordCount(key.key(), value, new Date(key.window().start()), new Date(key.window().end()))));
}
public static void main(String[] args) {
SpringApplication.run(WordCountProcessorApplication.class, args);
}</pre><p>Once built as a uber-jar (e.g., <code class="literal">wordcount-processor.jar</code>), you can run the above example like the following.</p><pre class="screen">java -jar wordcount-processor.jar --spring.cloud.stream.bindings.input.destination=words --spring.cloud.stream.bindings.output.destination=counts</pre><p>This application will consume messages from the Kafka topic <code class="literal">words</code> and the computed results are published to an output
topic <code class="literal">counts</code>.</p><p>Spring Cloud Stream will ensure that the messages from both the incoming and outgoing topics are automatically bound as
KStream objects. As a developer, you can exclusively focus on the business aspects of the code, i.e. writing the logic
required in the processor. Setting up the Streams DSL specific configuration required by the Kafka Streams infrastructure
is automatically handled by the framework.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_configuration_options_3" href="#_configuration_options_3"></a>39.3&nbsp;Configuration Options</h2></div></div></div><p>This section contains the configuration options used by the Kafka Streams binder.</p><p>For common configuration options and properties pertaining to binder, refer to the <a class="link" href="#binding-properties" title="30.2&nbsp;Binding Properties">core documentation</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_kafka_streams_properties" href="#_kafka_streams_properties"></a>39.3.1&nbsp;Kafka Streams Properties</h3></div></div></div><p>The following properties are available at the binder level and must be prefixed with <code class="literal">spring.cloud.stream.kafka.streams.binder.</code>
literal.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">configuration</span></dt><dd> Map with a key/value pair containing properties pertaining to Apache Kafka Streams API.
This property must be prefixed with <code class="literal">spring.cloud.stream.kafka.streams.binder.</code>.
Following are some examples of using this property.</dd></dl></div><pre class="screen">spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde=org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde=org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms=1000</pre><p>For more information about all the properties that may go into streams configuration, see StreamsConfig JavaDocs in
Apache Kafka Streams docs.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">brokers</span></dt><dd><p class="simpara">Broker URL</p><p class="simpara">Default: <code class="literal">localhost</code></p></dd><dt><span class="term">zkNodes</span></dt><dd><p class="simpara">Zookeeper URL</p><p class="simpara">Default: <code class="literal">localhost</code></p></dd><dt><span class="term">serdeError</span></dt><dd><p class="simpara">Deserialization error handler type.
Possible values are - <code class="literal">logAndContinue</code>, <code class="literal">logAndFail</code> or <code class="literal">sendToDlq</code></p><p class="simpara">Default: <code class="literal">logAndFail</code></p></dd><dt><span class="term">applicationId</span></dt><dd><p class="simpara">Convenient way to set the application.id for the Kafka Streams application globally at the binder level.
If the application contains multiple <code class="literal">StreamListener</code> methods, then application.id should be set at the binding level per input binding.</p><p class="simpara">Default: <code class="literal">none</code></p></dd></dl></div><p>The following properties are <span class="emphasis"><em>only</em></span> available for Kafka Streams producers and must be prefixed with <code class="literal">spring.cloud.stream.kafka.streams.bindings.&lt;binding name&gt;.producer.</code> literal.
For convenience, if there multiple output bindings and they all require a common value, that can be configured by using the prefix <code class="literal">spring.cloud.stream.kafka.streams.default.producer.</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">keySerde</span></dt><dd><p class="simpara">key serde to use</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">valueSerde</span></dt><dd><p class="simpara">value serde to use</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">useNativeEncoding</span></dt><dd><p class="simpara">flag to enable native encoding</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd></dl></div><p>The following properties are <span class="emphasis"><em>only</em></span> available for Kafka Streams consumers and must be prefixed with <code class="literal">spring.cloud.stream.kafka.streams.bindings.&lt;binding name&gt;.consumer.`literal.
For convenience, if there multiple input bindings and they all require a common value, that can be configured by using the prefix `spring.cloud.stream.kafka.streams.default.consumer.</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">applicationId</span></dt><dd><p class="simpara">Setting application.id per input binding.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">keySerde</span></dt><dd><p class="simpara">key serde to use</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">valueSerde</span></dt><dd><p class="simpara">value serde to use</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">materializedAs</span></dt><dd><p class="simpara">state store to materialize when using incoming KTable types</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">useNativeDecoding</span></dt><dd><p class="simpara">flag to enable native decoding</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">dlqName</span></dt><dd><p class="simpara">DLQ topic name.</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_timewindow_properties" href="#_timewindow_properties"></a>39.3.2&nbsp;TimeWindow properties:</h3></div></div></div><p>Windowing is an important concept in stream processing applications. Following properties are available to configure
time-window computations.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.kafka.streams.timeWindow.length</span></dt><dd><p class="simpara">When this property is given, you can autowire a <code class="literal">TimeWindows</code> bean into the application.
The value is expressed in milliseconds.</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.streams.timeWindow.advanceBy</span></dt><dd><p class="simpara">Value is given in milliseconds.</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd></dl></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_multiple_input_bindings" href="#_multiple_input_bindings"></a>39.4&nbsp;Multiple Input Bindings</h2></div></div></div><p>For use cases that requires multiple incoming KStream objects or a combination of KStream and KTable objects, the Kafka
Streams binder provides multiple bindings support.</p><p>Let&#8217;s see it in action.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_multiple_input_bindings_as_a_sink" href="#_multiple_input_bindings_as_a_sink"></a>39.4.1&nbsp;Multiple Input Bindings as a Sink</h3></div></div></div><pre class="screen">@EnableBinding(KStreamKTableBinding.class)
.....
.....
@StreamListener
public void process(@Input("inputStream") KStream&lt;String, PlayEvent&gt; playEvents,
@Input("inputTable") KTable&lt;Long, Song&gt; songTable) {
....
....
}
interface KStreamKTableBinding {
@Input("inputStream")
KStream&lt;?, ?&gt; inputStream();
@Input("inputTable")
KTable&lt;?, ?&gt; inputTable();
}</pre><p>In the above example, the application is written as a sink, i.e. there are no output bindings and the application has to
decide concerning downstream processing. When you write applications in this style, you might want to send the information
downstream or store them in a state store (See below for Queryable State Stores).</p><p>In the case of incoming KTable, if you want to materialize the computations to a state store, you have to express it
through the following property.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.inputTable.consumer.materializedAs: all-songs</pre><p>The above example shows the use of KTable as an input binding.
The binder also supports input bindings for GlobalKTable.
GlobalKTable binding is useful when you have to ensure that all instances of your application has access to the data updates from the topic.
KTable and GlobalKTable bindings are only available on the input.
Binder supports both input and output bindings for KStream.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_multiple_input_bindings_as_a_processor" href="#_multiple_input_bindings_as_a_processor"></a>39.4.2&nbsp;Multiple Input Bindings as a Processor</h3></div></div></div><pre class="screen">@EnableBinding(KStreamKTableBinding.class)
....
....
@StreamListener
@SendTo("output")
public KStream&lt;String, Long&gt; process(@Input("input") KStream&lt;String, Long&gt; userClicksStream,
@Input("inputTable") KTable&lt;String, String&gt; userRegionsTable) {
....
....
}
interface KStreamKTableBinding extends KafkaStreamsProcessor {
@Input("inputX")
KTable&lt;?, ?&gt; inputTable();
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_multiple_output_bindings_aka_branching" href="#_multiple_output_bindings_aka_branching"></a>39.5&nbsp;Multiple Output Bindings (aka Branching)</h2></div></div></div><p>Kafka Streams allow outbound data to be split into multiple topics based on some predicates. The Kafka Streams binder provides
support for this feature without compromising the programming model exposed through <code class="literal">StreamListener</code> in the end user application.</p><p>You can write the application in the usual way as demonstrated above in the word count example. However, when using the
branching feature, you are required to do a few things. First, you need to make sure that your return type is <code class="literal">KStream[]</code>
instead of a regular <code class="literal">KStream</code>. Second, you need to use the <code class="literal">SendTo</code> annotation containing the output bindings in the order
(see example below). For each of these output bindings, you need to configure destination, content-type etc., complying with
the standard Spring Cloud Stream expectations.</p><p>Here is an example:</p><pre class="screen">@EnableBinding(KStreamProcessorWithBranches.class)
@EnableAutoConfiguration
public static class WordCountProcessorApplication {
@Autowired
private TimeWindows timeWindows;
@StreamListener("input")
@SendTo({"output1","output2","output3})
public KStream&lt;?, WordCount&gt;[] process(KStream&lt;Object, String&gt; input) {
Predicate&lt;Object, WordCount&gt; isEnglish = (k, v) -&gt; v.word.equals("english");
Predicate&lt;Object, WordCount&gt; isFrench = (k, v) -&gt; v.word.equals("french");
Predicate&lt;Object, WordCount&gt; isSpanish = (k, v) -&gt; v.word.equals("spanish");
return input
.flatMapValues(value -&gt; Arrays.asList(value.toLowerCase().split("\\W+")))
.groupBy((key, value) -&gt; value)
.windowedBy(timeWindows)
.count(Materialized.as("WordCounts-1"))
.toStream()
.map((key, value) -&gt; new KeyValue&lt;&gt;(null, new WordCount(key.key(), value, new Date(key.window().start()), new Date(key.window().end()))))
.branch(isEnglish, isFrench, isSpanish);
}
interface KStreamProcessorWithBranches {
@Input("input")
KStream&lt;?, ?&gt; input();
@Output("output1")
KStream&lt;?, ?&gt; output1();
@Output("output2")
KStream&lt;?, ?&gt; output2();
@Output("output3")
KStream&lt;?, ?&gt; output3();
}
}</pre><p>Properties:</p><pre class="screen">spring.cloud.stream.bindings.output1.contentType: application/json
spring.cloud.stream.bindings.output2.contentType: application/json
spring.cloud.stream.bindings.output3.contentType: application/json
spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms: 1000
spring.cloud.stream.kafka.streams.binder.configuration:
default.key.serde: org.apache.kafka.common.serialization.Serdes$StringSerde
default.value.serde: org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.bindings.output1:
destination: foo
producer:
headerMode: raw
spring.cloud.stream.bindings.output2:
destination: bar
producer:
headerMode: raw
spring.cloud.stream.bindings.output3:
destination: fox
producer:
headerMode: raw
spring.cloud.stream.bindings.input:
destination: words
consumer:
headerMode: raw</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_message_conversion" href="#_message_conversion"></a>39.6&nbsp;Message Conversion</h2></div></div></div><p>Similar to message-channel based binder applications, the Kafka Streams binder adapts to the out-of-the-box content-type
conversions without any compromise.</p><p>It is typical for Kafka Streams operations to know the type of SerDe&#8217;s used to transform the key and value correctly.
Therefore, it may be more natural to rely on the SerDe facilities provided by the Apache Kafka Streams library itself at
the inbound and outbound conversions rather than using the content-type conversions offered by the framework.
On the other hand, you might be already familiar with the content-type conversion patterns provided by the framework, and
that, you&#8217;d like to continue using for inbound and outbound conversions.</p><p>Both the options are supported in the Kafka Streams binder implementation.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_outbound_serialization" href="#_outbound_serialization"></a>39.6.1&nbsp;Outbound serialization</h3></div></div></div><p>If native encoding is disabled (which is the default), then the framework will convert the message using the contentType
set by the user (otherwise, the default <code class="literal">application/json</code> will be applied). It will ignore any SerDe set on the outbound
in this case for outbound serialization.</p><p>Here is the property to set the contentType on the outbound.</p><pre class="screen">spring.cloud.stream.bindings.output.contentType: application/json</pre><p>Here is the property to enable native encoding.</p><pre class="screen">spring.cloud.stream.bindings.output.nativeEncoding: true</pre><p>If native encoding is enabled on the output binding (user has to enable it as above explicitly), then the framework will
skip any form of automatic message conversion on the outbound. In that case, it will switch to the Serde set by the user.
The <code class="literal">valueSerde</code> property set on the actual output binding will be used. Here is an example.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.output.producer.valueSerde: org.apache.kafka.common.serialization.Serdes$StringSerde</pre><p>If this property is not set, then it will use the "default" SerDe: <code class="literal">spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde</code>.</p><p>It is worth to mention that Kafka Streams binder does not serialize the keys on outbound - it simply relies on Kafka itself.
Therefore, you either have to specify the <code class="literal">keySerde</code> property on the binding or it will default to the application-wide common
<code class="literal">keySerde</code>.</p><p>Binding level key serde:</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.output.producer.keySerde</pre><p>Common Key serde:</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde</pre><p>If branching is used, then you need to use multiple output bindings. For example,</p><pre class="screen">interface KStreamProcessorWithBranches {
@Input("input")
KStream&lt;?, ?&gt; input();
@Output("output1")
KStream&lt;?, ?&gt; output1();
@Output("output2")
KStream&lt;?, ?&gt; output2();
@Output("output3")
KStream&lt;?, ?&gt; output3();
}</pre><p>If <code class="literal">nativeEncoding</code> is set, then you can set different SerDe&#8217;s on individual output bindings as below.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.output1.producer.valueSerde=IntegerSerde
spring.cloud.stream.kafka.streams.bindings.output2.producer.valueSerde=StringSerde
spring.cloud.stream.kafka.streams.bindings.output3.producer.valueSerde=JsonSerde</pre><p>Then if you have <code class="literal">SendTo</code> like this, @SendTo({"output1", "output2", "output3"}), the <code class="literal">KStream[]</code> from the branches are
applied with proper SerDe objects as defined above. If you are not enabling <code class="literal">nativeEncoding</code>, you can then set different
contentType values on the output bindings as below. In that case, the framework will use the appropriate message converter
to convert the messages before sending to Kafka.</p><pre class="screen">spring.cloud.stream.bindings.output1.contentType: application/json
spring.cloud.stream.bindings.output2.contentType: application/java-serialzied-object
spring.cloud.stream.bindings.output3.contentType: application/octet-stream</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_inbound_deserialization" href="#_inbound_deserialization"></a>39.6.2&nbsp;Inbound Deserialization</h3></div></div></div><p>Similar rules apply to data deserialization on the inbound.</p><p>If native decoding is disabled (which is the default), then the framework will convert the message using the contentType
set by the user (otherwise, the default <code class="literal">application/json</code> will be applied). It will ignore any SerDe set on the inbound
in this case for inbound deserialization.</p><p>Here is the property to set the contentType on the inbound.</p><pre class="screen">spring.cloud.stream.bindings.input.contentType: application/json</pre><p>Here is the property to enable native decoding.</p><pre class="screen">spring.cloud.stream.bindings.input.nativeDecoding: true</pre><p>If native decoding is enabled on the input binding (user has to enable it as above explicitly), then the framework will
skip doing any message conversion on the inbound. In that case, it will switch to the SerDe set by the user. The <code class="literal">valueSerde</code>
property set on the actual output binding will be used. Here is an example.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.input.consumer.valueSerde: org.apache.kafka.common.serialization.Serdes$StringSerde</pre><p>If this property is not set, it will use the default SerDe: <code class="literal">spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde</code>.</p><p>It is worth to mention that Kafka Streams binder does not deserialize the keys on inbound - it simply relies on Kafka itself.
Therefore, you either have to specify the <code class="literal">keySerde</code> property on the binding or it will default to the application-wide common
<code class="literal">keySerde</code>.</p><p>Binding level key serde:</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.input.consumer.keySerde</pre><p>Common Key serde:</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde</pre><p>As in the case of KStream branching on the outbound, the benefit of setting value SerDe per binding is that if you have
multiple input bindings (multiple KStreams object) and they all require separate value SerDe&#8217;s, then you can configure
them individually. If you use the common configuration approach, then this feature won&#8217;t be applicable.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_error_handling" href="#_error_handling"></a>39.7&nbsp;Error Handling</h2></div></div></div><p>Apache Kafka Streams provide the capability for natively handling exceptions from deserialization errors.
For details on this support, please see <a class="link" href="https://cwiki.apache.org/confluence/display/KAFKA/KIP-161%3A+streams+deserialization+exception+handlers" target="_top">this</a>
Out of the box, Apache Kafka Streams provide two kinds of deserialization exception handlers - <code class="literal">logAndContinue</code> and <code class="literal">logAndFail</code>.
As the name indicates, the former will log the error and continue processing the next records and the latter will log the
error and fail. <code class="literal">LogAndFail</code> is the default deserialization exception handler.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_handling_deserialization_exceptions" href="#_handling_deserialization_exceptions"></a>39.7.1&nbsp;Handling Deserialization Exceptions</h3></div></div></div><p>Kafka Streams binder supports a selection of exception handlers through the following properties.</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.serdeError: logAndContinue</pre><p>In addition to the above two deserialization exception handlers, the binder also provides a third one for sending the erroneous
records (poison pills) to a DLQ topic. Here is how you enable this DLQ exception handler.</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.serdeError: sendToDlq</pre><p>When the above property is set, all the deserialization error records are automatically sent to the DLQ topic.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.input.consumer.dlqName: foo-dlq</pre><p>If this is set, then the error records are sent to the topic <code class="literal">foo-dlq</code>. If this is not set, then it will create a DLQ
topic with the name <code class="literal">error.&lt;input-topic-name&gt;.&lt;group-name&gt;</code>.</p><p>A couple of things to keep in mind when using the exception handling feature in Kafka Streams binder.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The property <code class="literal">spring.cloud.stream.kafka.streams.binder.serdeError</code> is applicable for the entire application. This implies
that if there are multiple <code class="literal">StreamListener</code> methods in the same application, this property is applied to all of them.</li><li class="listitem">The exception handling for deserialization works consistently with native deserialization and framework provided message
conversion.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_handling_non_deserialization_exceptions" href="#_handling_non_deserialization_exceptions"></a>39.7.2&nbsp;Handling Non-Deserialization Exceptions</h3></div></div></div><p>For general error handling in Kafka Streams binder, it is up to the end user applications to handle application level errors.
As a side effect of providing a DLQ for deserialization exception handlers, Kafka Streams binder provides a way to get
access to the DLQ sending bean directly from your application.
Once you get access to that bean, you can programmatically send any exception records from your application to the DLQ.</p><p>It continues to remain hard to robust error handling using the high-level DSL; Kafka Streams doesn&#8217;t natively support error
handling yet.</p><p>However, when you use the low-level Processor API in your application, there are options to control this behavior. See
below.</p><pre class="screen">@Autowired
private SendToDlqAndContinue dlqHandler;
@StreamListener("input")
@SendTo("output")
public KStream&lt;?, WordCount&gt; process(KStream&lt;Object, String&gt; input) {
input.process(() -&gt; new Processor() {
ProcessorContext context;
@Override
public void init(ProcessorContext context) {
this.context = context;
}
@Override
public void process(Object o, Object o2) {
try {
.....
.....
}
catch(Exception e) {
//explicitly provide the kafka topic corresponding to the input binding as the first argument.
//DLQ handler will correctly map to the dlq topic from the actual incoming destination.
dlqHandler.sendToDlq("topic-name", (byte[]) o1, (byte[]) o2, context.partition());
}
}
.....
.....
});
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_state_store" href="#_state_store"></a>39.8&nbsp;State Store</h2></div></div></div><p>State store is created automatically by Kafka Streams when the DSL is used.
When processor API is used, you need to register a state store manually. In order to do so, you can use <code class="literal">KafkaStreamsStateStore</code> annotation.
You can specify the name and type of the store, flags to control log and disabling cache, etc.
Once the store is created by the binder during the bootstrapping phase, you can access this state store through the processor API.
Below are some primitives for doing this.</p><p>Creating a state store:</p><pre class="screen">@KafkaStreamsStateStore(name="mystate", type= KafkaStreamsStateStoreProperties.StoreType.WINDOW, lengthMs=300000)
public void process(KStream&lt;Object, Product&gt; input) {
...
}</pre><p>Accessing the state store:</p><pre class="screen">Processor&lt;Object, Product&gt;() {
WindowStore&lt;Object, String&gt; state;
@Override
public void init(ProcessorContext processorContext) {
state = (WindowStore)processorContext.getStateStore("mystate");
}
...
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_interactive_queries" href="#_interactive_queries"></a>39.9&nbsp;Interactive Queries</h2></div></div></div><p>As part of the public Kafka Streams binder API, we expose a class called <code class="literal">InteractiveQueryService</code>.
You can access this as a Spring bean in your application. An easy way to get access to this bean from your application is to "autowire" the bean.</p><pre class="screen">@Autowired
private InteractiveQueryService interactiveQueryService;</pre><p>Once you gain access to this bean, then you can query for the particular state-store that you are interested. See below.</p><pre class="screen">ReadOnlyKeyValueStore&lt;Object, Object&gt; keyValueStore =
interactiveQueryService.getQueryableStoreType("my-store", QueryableStoreTypes.keyValueStore());</pre><p>If there are multiple instances of the kafka streams application running, then before you can query them interactively, you need to identify which application instance hosts the key.
<code class="literal">InteractiveQueryService</code> API provides methods for identifying the host information.</p><p>In order for this to work, you must configure the property <code class="literal">application.server</code> as below:</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.configuration.application.server: &lt;server&gt;:&lt;port&gt;</pre><p>Here are some code snippets:</p><pre class="screen">org.apache.kafka.streams.state.HostInfo hostInfo = interactiveQueryService.getHostInfo("store-name",
key, keySerializer);
if (interactiveQueryService.getCurrentHostInfo().equals(hostInfo)) {
//query from the store that is locally available
}
else {
//query from the remote host
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_accessing_the_underlying_kafkastreams_object" href="#_accessing_the_underlying_kafkastreams_object"></a>39.10&nbsp;Accessing the underlying KafkaStreams object</h2></div></div></div><p><code class="literal">StreamBuilderFactoryBean</code> from spring-kafka that is responsible for constructing the <code class="literal">KafkaStreams</code> object can be accessed programmatically.
Each <code class="literal">StreamBuilderFactoryBean</code> is registered as <code class="literal">stream-builder</code> and appended with the <code class="literal">StreamListener</code> method name.
If your <code class="literal">StreamListener</code> method is named as <code class="literal">process</code> for example, the stream builder bean is named as <code class="literal">stream-builder-process</code>.
Since this is a factory bean, it should be accessed by prepending an ampersand (<code class="literal">&amp;</code>) when accessing it programmatically.
Following is an example and it assumes the <code class="literal">StreamListener</code> method is named as <code class="literal">process</code></p><pre class="screen">StreamsBuilderFactoryBean streamsBuilderFactoryBean = context.getBean("&amp;stream-builder-process", StreamsBuilderFactoryBean.class);
KafkaStreams kafkaStreams = streamsBuilderFactoryBean.getKafkaStreams();</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_state_cleanup" href="#_state_cleanup"></a>39.11&nbsp;State Cleanup</h2></div></div></div><p>By default, the <code class="literal">Kafkastreams.cleanup()</code> method is called when the binding is stopped.
See <a class="link" href="https://docs.spring.io/spring-kafka/reference/html/_reference.html#_configuration" target="_top">the Spring Kafka documentation</a>.
To modify this behavior simply add a single <code class="literal">CleanupConfig</code> <code class="literal">@Bean</code> (configured to clean up on start, stop, or neither) to the application context; the bean will be detected and wired into the factory bean.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_rabbitmq_binder" href="#_rabbitmq_binder"></a>40.&nbsp;RabbitMQ Binder</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_usage_3" href="#_usage_3"></a>40.1&nbsp;Usage</h2></div></div></div><p>To use the RabbitMQ binder, you can add it to your Spring Cloud Stream application, by using the following Maven coordinates:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-binder-rabbit<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>Alternatively, you can use the Spring Cloud Stream RabbitMQ Starter, as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-stream-rabbit<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_rabbitmq_binder_overview" href="#_rabbitmq_binder_overview"></a>40.2&nbsp;RabbitMQ Binder Overview</h2></div></div></div><p>The following simplified diagram shows how the RabbitMQ binder operates:</p><div class="figure"><a name="d0e13399" href="#d0e13399"></a><p class="title"><b>Figure&nbsp;40.1.&nbsp;RabbitMQ Binder</b></p><div class="figure-contents"><div class="mediaobject"><img src="images/rabbit-binder.png" alt="rabbit binder"></div></div></div><br class="figure-break"><p>By default, the RabbitMQ Binder implementation maps each destination to a <code class="literal">TopicExchange</code>.
For each consumer group, a <code class="literal">Queue</code> is bound to that <code class="literal">TopicExchange</code>.
Each consumer instance has a corresponding RabbitMQ <code class="literal">Consumer</code> instance for its group&#8217;s <code class="literal">Queue</code>.
For partitioned producers and consumers, the queues are suffixed with the partition index and use the partition index as the routing key.
For anonymous consumers (those with no <code class="literal">group</code> property), an auto-delete queue (with a randomized unique name) is used.</p><p>By using the optional <code class="literal">autoBindDlq</code> option, you can configure the binder to create and configure dead-letter queues (DLQs) (and a dead-letter exchange <code class="literal">DLX</code>, as well as routing infrastructure).
By default, the dead letter queue has the name of the destination, appended with <code class="literal">.dlq</code>.
If retry is enabled (<code class="literal">maxAttempts &gt; 1</code>), failed messages are delivered to the DLQ after retries are exhausted.
If retry is disabled (<code class="literal">maxAttempts = 1</code>), you should set <code class="literal">requeueRejected</code> to <code class="literal">false</code> (the default) so that failed messages are routed to the DLQ, instead of being re-queued.
In addition, <code class="literal">republishToDlq</code> causes the binder to publish a failed message to the DLQ (instead of rejecting it).
This feature lets additional information (such as the stack trace in the <code class="literal">x-exception-stacktrace</code> header) be added to the message in headers.
This option does not need retry enabled.
You can republish a failed message after just one attempt.
Starting with version 1.2, you can configure the delivery mode of republished messages.
See the <a class="link" href="#spring-cloud-stream-rabbit-republish-delivery-mode"><code class="literal">republishDeliveryMode</code> property</a>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Setting <code class="literal">requeueRejected</code> to <code class="literal">true</code> (with <code class="literal">republishToDlq=false</code> ) causes the message to be re-queued and redelivered continually, which is likely not what you want unless the reason for the failure is transient.
In general, you should enable retry within the binder by setting <code class="literal">maxAttempts</code> to greater than one or by setting <code class="literal">republishToDlq</code> to <code class="literal">true</code>.</p></td></tr></table></div><p>See <a class="xref" href="#rabbit-binder-properties" title="40.3.1&nbsp;RabbitMQ Binder Properties">Section&nbsp;40.3.1, &#8220;RabbitMQ Binder Properties&#8221;</a> for more information about these properties.</p><p>The framework does not provide any standard mechanism to consume dead-letter messages (or to re-route them back to the primary queue).
Some options are described in <a class="xref" href="#rabbit-dlq-processing" title="40.6&nbsp;Dead-Letter Queue Processing">Section&nbsp;40.6, &#8220;Dead-Letter Queue Processing&#8221;</a>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When multiple RabbitMQ binders are used in a Spring Cloud Stream application, it is important to disable 'RabbitAutoConfiguration' to avoid the same configuration from <code class="literal">RabbitAutoConfiguration</code> being applied to the two binders.
You can exclude the class by using the <code class="literal">@SpringBootApplication</code> annotation.</p></td></tr></table></div><p>Starting with version 2.0, the <code class="literal">RabbitMessageChannelBinder</code> sets the <code class="literal">RabbitTemplate.userPublisherConnection</code> property to <code class="literal">true</code> so that the non-transactional producers avoid deadlocks on consumers, which can happen if cached connections are blocked because of a <a class="link" href="https://www.rabbitmq.com/memory.html" target="_top">memory alarm</a> on the broker.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Currently, a <code class="literal">multiplex</code> consumer (a single consumer listening to multiple queues) is only supported for message-driven conssumers; polled consumers can only retrieve messages from a single queue.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_configuration_options_4" href="#_configuration_options_4"></a>40.3&nbsp;Configuration Options</h2></div></div></div><p>This section contains settings specific to the RabbitMQ Binder and bound channels.</p><p>For general binding configuration options and properties, see the <a class="link" href="https://github.com/spring-cloud/spring-cloud-stream/blob/master/spring-cloud-stream-core-docs/src/main/asciidoc/spring-cloud-stream-overview.adoc#configuration-options" target="_top">Spring Cloud Stream core documentation</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="rabbit-binder-properties" href="#rabbit-binder-properties"></a>40.3.1&nbsp;RabbitMQ Binder Properties</h3></div></div></div><p>By default, the RabbitMQ binder uses Spring Boot&#8217;s <code class="literal">ConnectionFactory</code>.
Conseuqently, it supports all Spring Boot configuration options for RabbitMQ.
(For reference, see the <a class="link" href="http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#common-application-properties" target="_top">Spring Boot documentation</a>).
RabbitMQ configuration options use the <code class="literal">spring.rabbitmq</code> prefix.</p><p>In addition to Spring Boot options, the RabbitMQ binder supports the following properties:</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.rabbit.binder.adminAddresses</span></dt><dd><p class="simpara">A comma-separated list of RabbitMQ management plugin URLs.
Only used when <code class="literal">nodes</code> contains more than one entry.
Each entry in this list must have a corresponding entry in <code class="literal">spring.rabbitmq.addresses</code>.
Only needed if you use a RabbitMQ cluster and wish to consume from the node that hosts the queue.
See <a class="link" href="https://docs.spring.io/spring-amqp/reference/html/_reference.html#queue-affinity" target="_top">Queue Affinity and the LocalizedQueueConnectionFactory</a> for more information.</p><p class="simpara">Default: empty.</p></dd><dt><span class="term">spring.cloud.stream.rabbit.binder.nodes</span></dt><dd><p class="simpara">A comma-separated list of RabbitMQ node names.
When more than one entry, used to locate the server address where a queue is located.
Each entry in this list must have a corresponding entry in <code class="literal">spring.rabbitmq.addresses</code>.
Only needed if you use a RabbitMQ cluster and wish to consume from the node that hosts the queue.
See <a class="link" href="https://docs.spring.io/spring-amqp/reference/html/_reference.html#queue-affinity" target="_top">Queue Affinity and the LocalizedQueueConnectionFactory</a> for more information.</p><p class="simpara">Default: empty.</p></dd><dt><span class="term">spring.cloud.stream.rabbit.binder.compressionLevel</span></dt><dd><p class="simpara">The compression level for compressed bindings.
See <code class="literal">java.util.zip.Deflater</code>.</p><p class="simpara">Default: <code class="literal">1</code> (BEST_LEVEL).</p></dd><dt><span class="term">spring.cloud.stream.binder.connection-name-prefix</span></dt><dd><p class="simpara">A connection name prefix used to name the connection(s) created by this binder.
The name is this prefix followed by <code class="literal">#n</code>, where <code class="literal">n</code> increments each time a new connection is opened.</p><p class="simpara">Default: none (Spring AMQP default).</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_rabbitmq_consumer_properties" href="#_rabbitmq_consumer_properties"></a>40.3.2&nbsp;RabbitMQ Consumer Properties</h3></div></div></div><p>The following properties are available for Rabbit consumers only and must be prefixed with <code class="literal">spring.cloud.stream.rabbit.bindings.&lt;channelName&gt;.consumer.</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">acknowledgeMode</span></dt><dd><p class="simpara">The acknowledge mode.</p><p class="simpara">Default: <code class="literal">AUTO</code>.</p></dd><dt><span class="term">autoBindDlq</span></dt><dd><p class="simpara">Whether to automatically declare the DLQ and bind it to the binder DLX.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">bindingRoutingKey</span></dt><dd><p class="simpara">The routing key with which to bind the queue to the exchange (if <code class="literal">bindQueue</code> is <code class="literal">true</code>).
For partitioned destinations, <code class="literal">-&lt;instanceIndex&gt;</code> is appended.</p><p class="simpara">Default: <code class="literal">#</code>.</p></dd><dt><span class="term">bindQueue</span></dt><dd><p class="simpara">Whether to bind the queue to the destination exchange.
Set it to <code class="literal">false</code> if you have set up your own infrastructure and have previously created and bound the queue.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">consumerTagPrefix</span></dt><dd><p class="simpara">Used to create the consumer tag(s); will be appended by <code class="literal">#n</code> where <code class="literal">n</code> increments for each consumer created.
Example: <code class="literal">${spring.application.name}-${spring.cloud.stream.bindings.input.group}-${spring.cloud.stream.instance-index}</code>.</p><p class="simpara">Default: none - the broker will generate random consumer tags.</p></dd><dt><span class="term">deadLetterQueueName</span></dt><dd><p class="simpara">The name of the DLQ</p><p class="simpara">Default: <code class="literal">prefix+destination.dlq</code></p></dd><dt><span class="term">deadLetterExchange</span></dt><dd><p class="simpara">A DLX to assign to the queue.
Relevant only if <code class="literal">autoBindDlq</code> is <code class="literal">true</code>.</p><p class="simpara">Default: 'prefix+DLX'</p></dd><dt><span class="term">deadLetterExchangeType</span></dt><dd><p class="simpara">The type of the DLX to assign to the queue.
Relevant only if <code class="literal">autoBindDlq</code> is <code class="literal">true</code>.</p><p class="simpara">Default: 'direct'</p></dd><dt><span class="term">deadLetterRoutingKey</span></dt><dd><p class="simpara">A dead letter routing key to assign to the queue.
Relevant only if <code class="literal">autoBindDlq</code> is <code class="literal">true</code>.</p><p class="simpara">Default: <code class="literal">destination</code></p></dd><dt><span class="term">declareDlx</span></dt><dd><p class="simpara">Whether to declare the dead letter exchange for the destination.
Relevant only if <code class="literal">autoBindDlq</code> is <code class="literal">true</code>.
Set to <code class="literal">false</code> if you have a pre-configured DLX.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">declareExchange</span></dt><dd><p class="simpara">Whether to declare the exchange for the destination.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">delayedExchange</span></dt><dd><p class="simpara">Whether to declare the exchange as a <code class="literal">Delayed Message Exchange</code>.
Requires the delayed message exchange plugin on the broker.
The <code class="literal">x-delayed-type</code> argument is set to the <code class="literal">exchangeType</code>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">dlqDeadLetterExchange</span></dt><dd><p class="simpara">If a DLQ is declared, a DLX to assign to that queue.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">dlqDeadLetterRoutingKey</span></dt><dd><p class="simpara">If a DLQ is declared, a dead letter routing key to assign to that queue.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">dlqExpires</span></dt><dd><p class="simpara">How long before an unused dead letter queue is deleted (in milliseconds).</p><p class="simpara">Default: <code class="literal">no expiration</code></p></dd><dt><span class="term">dlqLazy</span></dt><dd><p class="simpara">Declare the dead letter queue with the <code class="literal">x-queue-mode=lazy</code> argument.
See <a class="link" href="https://www.rabbitmq.com/lazy-queues.html" target="_top">&#8220;Lazy Queues&#8221;</a>.
Consider using a policy instead of this setting, because using a policy allows changing the setting without deleting the queue.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">dlqMaxLength</span></dt><dd><p class="simpara">Maximum number of messages in the dead letter queue.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">dlqMaxLengthBytes</span></dt><dd><p class="simpara">Maximum number of total bytes in the dead letter queue from all messages.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">dlqMaxPriority</span></dt><dd><p class="simpara">Maximum priority of messages in the dead letter queue (0-255).</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">dlqOverflowBehavior</span></dt><dd><p class="simpara">Action to take when <code class="literal">dlqMaxLength</code> or <code class="literal">dlqMaxLengthBytes</code> is exceeded; currently <code class="literal">drop-head</code> or <code class="literal">reject-publish</code> but refer to the RabbitMQ documentation.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">dlqTtl</span></dt><dd><p class="simpara">Default time to live to apply to the dead letter queue when declared (in milliseconds).</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">durableSubscription</span></dt><dd><p class="simpara">Whether the subscription should be durable.
Only effective if <code class="literal">group</code> is also set.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">exchangeAutoDelete</span></dt><dd><p class="simpara">If <code class="literal">declareExchange</code> is true, whether the exchange should be auto-deleted (that is, removed after the last queue is removed).</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">exchangeDurable</span></dt><dd><p class="simpara">If <code class="literal">declareExchange</code> is true, whether the exchange should be durable (that is, it survives broker restart).</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">exchangeType</span></dt><dd><p class="simpara">The exchange type: <code class="literal">direct</code>, <code class="literal">fanout</code> or <code class="literal">topic</code> for non-partitioned destinations and <code class="literal">direct</code> or <code class="literal">topic</code> for partitioned destinations.</p><p class="simpara">Default: <code class="literal">topic</code>.</p></dd><dt><span class="term">exclusive</span></dt><dd><p class="simpara">Whether to create an exclusive consumer.
Concurrency should be 1 when this is <code class="literal">true</code>.
Often used when strict ordering is required but enabling a hot standby instance to take over after a failure.
See <code class="literal">recoveryInterval</code>, which controls how often a standby instance attempts to consume.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">expires</span></dt><dd><p class="simpara">How long before an unused queue is deleted (in milliseconds).</p><p class="simpara">Default: <code class="literal">no expiration</code></p></dd><dt><span class="term">failedDeclarationRetryInterval</span></dt><dd><p class="simpara">The interval (in milliseconds) between attempts to consume from a queue if it is missing.</p><p class="simpara">Default: 5000</p></dd><dt><span class="term">headerPatterns</span></dt><dd><p class="simpara">Patterns for headers to be mapped from inbound messages.</p><p class="simpara">Default: <code class="literal">['*']</code> (all headers).</p></dd><dt><span class="term">lazy</span></dt><dd><p class="simpara">Declare the queue with the <code class="literal">x-queue-mode=lazy</code> argument.
See <a class="link" href="https://www.rabbitmq.com/lazy-queues.html" target="_top">&#8220;Lazy Queues&#8221;</a>.
Consider using a policy instead of this setting, because using a policy allows changing the setting without deleting the queue.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">maxConcurrency</span></dt><dd><p class="simpara">The maximum number of consumers.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">maxLength</span></dt><dd><p class="simpara">The maximum number of messages in the queue.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">maxLengthBytes</span></dt><dd><p class="simpara">The maximum number of total bytes in the queue from all messages.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">maxPriority</span></dt><dd><p class="simpara">The maximum priority of messages in the queue (0-255).</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">missingQueuesFatal</span></dt><dd><p class="simpara">When the queue cannot be found, whether to treat the condition as fatal and stop the listener container.
Defaults to <code class="literal">false</code> so that the container keeps trying to consume from the queue&#8201;&#8212;&#8201;for example, when using a cluster and the node hosting a non-HA queue is down.</p><p class="simpara">Default: <code class="literal">false</code></p></dd><dt><span class="term">overflowBehavior</span></dt><dd><p class="simpara">Action to take when <code class="literal">maxLength</code> or <code class="literal">maxLengthBytes</code> is exceeded; currently <code class="literal">drop-head</code> or <code class="literal">reject-publish</code> but refer to the RabbitMQ documentation.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">prefetch</span></dt><dd><p class="simpara">Prefetch count.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">prefix</span></dt><dd><p class="simpara">A prefix to be added to the name of the <code class="literal">destination</code> and queues.</p><p class="simpara">Default: "".</p></dd><dt><span class="term">queueDeclarationRetries</span></dt><dd><p class="simpara">The number of times to retry consuming from a queue if it is missing.
Relevant only when <code class="literal">missingQueuesFatal</code> is <code class="literal">true</code>.
Otherwise, the container keeps retrying indefinitely.</p><p class="simpara">Default: <code class="literal">3</code></p></dd><dt><span class="term">queueNameGroupOnly</span></dt><dd><p class="simpara">When true, consume from a queue with a name equal to the <code class="literal">group</code>.
Otherwise the queue name is <code class="literal">destination.group</code>.
This is useful, for example, when using Spring Cloud Stream to consume from an existing RabbitMQ queue.</p><p class="simpara">Default: false.</p></dd><dt><span class="term">recoveryInterval</span></dt><dd><p class="simpara">The interval between connection recovery attempts, in milliseconds.</p><p class="simpara">Default: <code class="literal">5000</code>.</p></dd><dt><span class="term">requeueRejected</span></dt><dd><p class="simpara">Whether delivery failures should be re-queued when retry is disabled or <code class="literal">republishToDlq</code> is <code class="literal">false</code>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd></dl></div><div class="variablelist"><a name="spring-cloud-stream-rabbit-republish-delivery-mode" href="#spring-cloud-stream-rabbit-republish-delivery-mode"></a><dl class="variablelist"><dt><span class="term">republishDeliveryMode</span></dt><dd><p class="simpara">When <code class="literal">republishToDlq</code> is <code class="literal">true</code>, specifies the delivery mode of the republished message.</p><p class="simpara">Default: <code class="literal">DeliveryMode.PERSISTENT</code></p></dd><dt><span class="term">republishToDlq</span></dt><dd><p class="simpara">By default, messages that fail after retries are exhausted are rejected.
If a dead-letter queue (DLQ) is configured, RabbitMQ routes the failed message (unchanged) to the DLQ.
If set to <code class="literal">true</code>, the binder republishs failed messages to the DLQ with additional headers, including the exception message and stack trace from the cause of the final failure.</p><p class="simpara">Default: false</p></dd><dt><span class="term">transacted</span></dt><dd><p class="simpara">Whether to use transacted channels.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">ttl</span></dt><dd><p class="simpara">Default time to live to apply to the queue when declared (in milliseconds).</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">txSize</span></dt><dd><p class="simpara">The number of deliveries between acks.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_advanced_listener_container_configuration" href="#_advanced_listener_container_configuration"></a>40.3.3&nbsp;Advanced Listener Container Configuration</h3></div></div></div><p>To set listener container properties that are not exposed as binder or binding properties, add a single bean of type <code class="literal">ListenerContainerCustomizer</code> to the application context.
The binder and binding properties will be set and then the customizer will be called.
The customizer (<code class="literal">configure()</code> method) is provided with the queue name as well as the consumer group as arguments.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_rabbit_producer_properties" href="#_rabbit_producer_properties"></a>40.3.4&nbsp;Rabbit Producer Properties</h3></div></div></div><p>The following properties are available for Rabbit producers only and
must be prefixed with <code class="literal">spring.cloud.stream.rabbit.bindings.&lt;channelName&gt;.producer.</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">autoBindDlq</span></dt><dd><p class="simpara">Whether to automatically declare the DLQ and bind it to the binder DLX.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">batchingEnabled</span></dt><dd><p class="simpara">Whether to enable message batching by producers.
Messages are batched into one message according to the following properties (described in the next three entries in this list): 'batchSize', <code class="literal">batchBufferLimit</code>, and <code class="literal">batchTimeout</code>.
See <a class="link" href="https://docs.spring.io/spring-amqp//reference/html/_reference.html#template-batching" target="_top">Batching</a> for more information.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">batchSize</span></dt><dd><p class="simpara">The number of messages to buffer when batching is enabled.</p><p class="simpara">Default: <code class="literal">100</code>.</p></dd><dt><span class="term">batchBufferLimit</span></dt><dd><p class="simpara">The maximum buffer size when batching is enabled.</p><p class="simpara">Default: <code class="literal">10000</code>.</p></dd><dt><span class="term">batchTimeout</span></dt><dd><p class="simpara">The batch timeout when batching is enabled.</p><p class="simpara">Default: <code class="literal">5000</code>.</p></dd><dt><span class="term">bindingRoutingKey</span></dt><dd><p class="simpara">The routing key with which to bind the queue to the exchange (if <code class="literal">bindQueue</code> is <code class="literal">true</code>).
Only applies to non-partitioned destinations.
Only applies if <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">#</code>.</p></dd><dt><span class="term">bindQueue</span></dt><dd><p class="simpara">Whether to bind the queue to the destination exchange.
Set it to <code class="literal">false</code> if you have set up your own infrastructure and have previously created and bound the queue.
Only applies if <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">compress</span></dt><dd><p class="simpara">Whether data should be compressed when sent.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">deadLetterQueueName</span></dt><dd><p class="simpara">The name of the DLQ
Only applies if <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">prefix+destination.dlq</code></p></dd><dt><span class="term">deadLetterExchange</span></dt><dd><p class="simpara">A DLX to assign to the queue.
Relevant only when <code class="literal">autoBindDlq</code> is <code class="literal">true</code>.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: 'prefix+DLX'</p></dd><dt><span class="term">deadLetterExchangeType</span></dt><dd><p class="simpara">The type of the DLX to assign to the queue.
Relevant only if <code class="literal">autoBindDlq</code> is <code class="literal">true</code>.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: 'direct'</p></dd><dt><span class="term">deadLetterRoutingKey</span></dt><dd><p class="simpara">A dead letter routing key to assign to the queue.
Relevant only when <code class="literal">autoBindDlq</code> is <code class="literal">true</code>.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">destination</code></p></dd><dt><span class="term">declareDlx</span></dt><dd><p class="simpara">Whether to declare the dead letter exchange for the destination.
Relevant only if <code class="literal">autoBindDlq</code> is <code class="literal">true</code>.
Set to <code class="literal">false</code> if you have a pre-configured DLX.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">declareExchange</span></dt><dd><p class="simpara">Whether to declare the exchange for the destination.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">delayExpression</span></dt><dd><p class="simpara">A SpEL expression to evaluate the delay to apply to the message (<code class="literal">x-delay</code> header).
It has no effect if the exchange is not a delayed message exchange.</p><p class="simpara">Default: No <code class="literal">x-delay</code> header is set.</p></dd><dt><span class="term">delayedExchange</span></dt><dd><p class="simpara">Whether to declare the exchange as a <code class="literal">Delayed Message Exchange</code>.
Requires the delayed message exchange plugin on the broker.
The <code class="literal">x-delayed-type</code> argument is set to the <code class="literal">exchangeType</code>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">deliveryMode</span></dt><dd><p class="simpara">The delivery mode.</p><p class="simpara">Default: <code class="literal">PERSISTENT</code>.</p></dd><dt><span class="term">dlqDeadLetterExchange</span></dt><dd><p class="simpara">When a DLQ is declared, a DLX to assign to that queue.
Applies only if <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">dlqDeadLetterRoutingKey</span></dt><dd><p class="simpara">When a DLQ is declared, a dead letter routing key to assign to that queue.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">dlqExpires</span></dt><dd><p class="simpara">How long (in milliseconds) before an unused dead letter queue is deleted.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">no expiration</code></p></dd><dt><span class="term">dlqLazy</span></dt><dd>Declare the dead letter queue with the <code class="literal">x-queue-mode=lazy</code> argument.
See <a class="link" href="https://www.rabbitmq.com/lazy-queues.html" target="_top">&#8220;Lazy Queues&#8221;</a>.
Consider using a policy instead of this setting, because using a policy allows changing the setting without deleting the queue.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</dd><dt><span class="term">dlqMaxLength</span></dt><dd><p class="simpara">Maximum number of messages in the dead letter queue.
Applies only if <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">dlqMaxLengthBytes</span></dt><dd><p class="simpara">Maximum number of total bytes in the dead letter queue from all messages.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">dlqMaxPriority</span></dt><dd><p class="simpara">Maximum priority of messages in the dead letter queue (0-255)
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">dlqTtl</span></dt><dd><p class="simpara">Default time (in milliseconds) to live to apply to the dead letter queue when declared.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">exchangeAutoDelete</span></dt><dd><p class="simpara">If <code class="literal">declareExchange</code> is <code class="literal">true</code>, whether the exchange should be auto-delete (it is removed after the last queue is removed).</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">exchangeDurable</span></dt><dd><p class="simpara">If <code class="literal">declareExchange</code> is <code class="literal">true</code>, whether the exchange should be durable (survives broker restart).</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">exchangeType</span></dt><dd><p class="simpara">The exchange type: <code class="literal">direct</code>, <code class="literal">fanout</code> or <code class="literal">topic</code> for non-partitioned destinations and <code class="literal">direct</code> or <code class="literal">topic</code> for partitioned destinations.</p><p class="simpara">Default: <code class="literal">topic</code>.</p></dd><dt><span class="term">expires</span></dt><dd><p class="simpara">How long (in milliseconds) before an unused queue is deleted.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">no expiration</code></p></dd><dt><span class="term">headerPatterns</span></dt><dd><p class="simpara">Patterns for headers to be mapped to outbound messages.</p><p class="simpara">Default: <code class="literal">['*']</code> (all headers).</p></dd><dt><span class="term">lazy</span></dt><dd><p class="simpara">Declare the queue with the <code class="literal">x-queue-mode=lazy</code> argument.
See <a class="link" href="https://www.rabbitmq.com/lazy-queues.html" target="_top">&#8220;Lazy Queues&#8221;</a>.
Consider using a policy instead of this setting, because using a policy allows changing the setting without deleting the queue.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">maxLength</span></dt><dd><p class="simpara">Maximum number of messages in the queue.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">maxLengthBytes</span></dt><dd><p class="simpara">Maximum number of total bytes in the queue from all messages.
Only applies if <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd><dt><span class="term">maxPriority</span></dt><dd><p class="simpara">Maximum priority of messages in the queue (0-255).
Only applies if <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">prefix</span></dt><dd><p class="simpara">A prefix to be added to the name of the <code class="literal">destination</code> exchange.</p><p class="simpara">Default: "".</p></dd><dt><span class="term">queueNameGroupOnly</span></dt><dd><p class="simpara">When <code class="literal">true</code>, consume from a queue with a name equal to the <code class="literal">group</code>.
Otherwise the queue name is <code class="literal">destination.group</code>.
This is useful, for example, when using Spring Cloud Stream to consume from an existing RabbitMQ queue.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: false.</p></dd><dt><span class="term">routingKeyExpression</span></dt><dd><p class="simpara">A SpEL expression to determine the routing key to use when publishing messages.
For a fixed routing key, use a literal expression, such as <code class="literal">routingKeyExpression='my.routingKey'</code> in a properties file or <code class="literal">routingKeyExpression: '''my.routingKey'''</code> in a YAML file.</p><p class="simpara">Default: <code class="literal">destination</code> or <code class="literal">destination-&lt;partition&gt;</code> for partitioned destinations.</p></dd><dt><span class="term">transacted</span></dt><dd><p class="simpara">Whether to use transacted channels.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">ttl</span></dt><dd><p class="simpara">Default time (in milliseconds) to live to apply to the queue when declared.
Applies only when <code class="literal">requiredGroups</code> are provided and then only to those groups.</p><p class="simpara">Default: <code class="literal">no limit</code></p></dd></dl></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>In the case of RabbitMQ, content type headers can be set by external applications.
Spring Cloud Stream supports them as part of an extended internal protocol used for any type of transport&#8201;&#8212;&#8201;including transports, such as Kafka (prior to 0.11), that do not natively support headers.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_retry_with_the_rabbitmq_binder" href="#_retry_with_the_rabbitmq_binder"></a>40.4&nbsp;Retry With the RabbitMQ Binder</h2></div></div></div><p>When retry is enabled within the binder, the listener container thread is suspended for any back off periods that are configured.
This might be important when strict ordering is required with a single consumer. However, for other use cases, it prevents other messages from being processed on that thread.
An alternative to using binder retry is to set up dead lettering with time to live on the dead-letter queue (DLQ) as well as dead-letter configuration on the DLQ itself.
See &#8220;<a class="xref" href="#rabbit-binder-properties" title="40.3.1&nbsp;RabbitMQ Binder Properties">Section&nbsp;40.3.1, &#8220;RabbitMQ Binder Properties&#8221;</a>&#8221; for more information about the properties discussed here.
You can use the following example configuration to enable this feature:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Set <code class="literal">autoBindDlq</code> to <code class="literal">true</code>.
The binder create a DLQ.
Optionally, you can specify a name in <code class="literal">deadLetterQueueName</code>.</li><li class="listitem">Set <code class="literal">dlqTtl</code> to the back off time you want to wait between redeliveries.</li><li class="listitem">Set the <code class="literal">dlqDeadLetterExchange</code> to the default exchange.
Expired messages from the DLQ are routed to the original queue, because the default <code class="literal">deadLetterRoutingKey</code> is the queue name (<code class="literal">destination.group</code>).
Setting to the default exchange is achieved by setting the property with no value, as shown in the next example.</li></ul></div><p>To force a message to be dead-lettered, either throw an <code class="literal">AmqpRejectAndDontRequeueException</code> or set <code class="literal">requeueRejected</code> to <code class="literal">true</code> (the default) and throw any exception.</p><p>The loop continue without end, which is fine for transient problems, but you may want to give up after some number of attempts.
Fortunately, RabbitMQ provides the <code class="literal">x-death</code> header, which lets you determine how many cycles have occurred.</p><p>To acknowledge a message after giving up, throw an <code class="literal">ImmediateAcknowledgeAmqpException</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_putting_it_all_together" href="#_putting_it_all_together"></a>40.4.1&nbsp;Putting it All Together</h3></div></div></div><p>The following configuration creates an exchange <code class="literal">myDestination</code> with queue <code class="literal">myDestination.consumerGroup</code> bound to a topic exchange with a wildcard routing key <code class="literal">#</code>:</p><pre class="screen">---
spring.cloud.stream.bindings.input.destination=myDestination
spring.cloud.stream.bindings.input.group=consumerGroup
#disable binder retries
spring.cloud.stream.bindings.input.consumer.max-attempts=1
#dlx/dlq setup
spring.cloud.stream.rabbit.bindings.input.consumer.auto-bind-dlq=true
spring.cloud.stream.rabbit.bindings.input.consumer.dlq-ttl=5000
spring.cloud.stream.rabbit.bindings.input.consumer.dlq-dead-letter-exchange=
---</pre><p>This configuration creates a DLQ bound to a direct exchange (<code class="literal">DLX</code>) with a routing key of <code class="literal">myDestination.consumerGroup</code>.
When messages are rejected, they are routed to the DLQ.
After 5 seconds, the message expires and is routed to the original queue by using the queue name as the routing key, as shown in the following example:</p><p><b>Spring Boot application.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> XDeathApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(XDeathApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> listen(String in, <em><span class="hl-annotation" style="color: gray">@Header(name = "x-death", required = false)</span></em> Map&lt;?,?&gt; death) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (death != null &amp;&amp; death.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"count"</span>).equals(<span class="hl-number">3L</span>)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// giving up - don't send to DLX</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ImmediateAcknowledgeAmqpException(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Failed after 4 attempts"</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AmqpRejectAndDontRequeueException(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"failed"</span>);
}
}</pre><p>
</p><p>Notice that the count property in the <code class="literal">x-death</code> header is a <code class="literal">Long</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="rabbit-error-channels" href="#rabbit-error-channels"></a>40.5&nbsp;Error Channels</h2></div></div></div><p>Starting with version 1.3, the binder unconditionally sends exceptions to an error channel for each consumer destination and can also be configured to send async producer send failures to an error channel.
See &#8220;<a class="xref" href="#">???</a>&#8221; for more information.</p><p>RabbitMQ has two types of send failures:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Returned messages,</li><li class="listitem">Negatively acknowledged <a class="link" href="https://www.rabbitmq.com/confirms.html" target="_top">Publisher Confirms</a>.</li></ul></div><p>The latter is rare.
According to the RabbitMQ documentation "[A nack] will only be delivered if an internal error occurs in the Erlang process responsible for a queue.".</p><p>As well as enabling producer error channels (as described in &#8220;<a class="xref" href="#">???</a>&#8221;), the RabbitMQ binder only sends messages to the channels if the connection factory is appropriately configured, as follows:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">ccf.setPublisherConfirms(true);</code></li><li class="listitem"><code class="literal">ccf.setPublisherReturns(true);</code></li></ul></div><p>When using Spring Boot configuration for the connection factory, set the following properties:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">spring.rabbitmq.publisher-confirms</code></li><li class="listitem"><code class="literal">spring.rabbitmq.publisher-returns</code></li></ul></div><p>The payload of the <code class="literal">ErrorMessage</code> for a returned message is a <code class="literal">ReturnedAmqpMessageException</code> with the following properties:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">failedMessage</code>: The spring-messaging <code class="literal">Message&lt;?&gt;</code> that failed to be sent.</li><li class="listitem"><code class="literal">amqpMessage</code>: The raw spring-amqp <code class="literal">Message</code>.</li><li class="listitem"><code class="literal">replyCode</code>: An integer value indicating the reason for the failure (for example, 312 - No route).</li><li class="listitem"><code class="literal">replyText</code>: A text value indicating the reason for the failure (for example, <code class="literal">NO_ROUTE</code>).</li><li class="listitem"><code class="literal">exchange</code>: The exchange to which the message was published.</li><li class="listitem"><code class="literal">routingKey</code>: The routing key used when the message was published.</li></ul></div><p>For negatively acknowledged confirmations, the payload is a <code class="literal">NackedAmqpMessageException</code> with the following properties:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">failedMessage</code>: The spring-messaging <code class="literal">Message&lt;?&gt;</code> that failed to be sent.</li><li class="listitem"><code class="literal">nackReason</code>: A reason (if available&#8201;&#8212;&#8201;you may need to examine the broker logs for more information).</li></ul></div><p>There is no automatic handling of these exceptions (such as sending to a <a class="link" href="#rabbit-dlq-processing" title="40.6&nbsp;Dead-Letter Queue Processing">dead-letter queue</a>).
You can consume these exceptions with your own Spring Integration flow.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="rabbit-dlq-processing" href="#rabbit-dlq-processing"></a>40.6&nbsp;Dead-Letter Queue Processing</h2></div></div></div><p>Because you cannot anticipate how users would want to dispose of dead-lettered messages, the framework does not provide any standard mechanism to handle them.
If the reason for the dead-lettering is transient, you may wish to route the messages back to the original queue.
However, if the problem is a permanent issue, that could cause an infinite loop.
The following Spring Boot application shows an example of how to route those messages back to the original queue but moves them to a third &#8220;parking lot&#8221; queue after three attempts.
The second example uses the <a class="link" href="https://www.rabbitmq.com/blog/2015/04/16/scheduling-messages-with-rabbitmq/" target="_top">RabbitMQ Delayed Message Exchange</a> to introduce a delay to the re-queued message.
In this example, the delay increases for each attempt.
These examples use a <code class="literal">@RabbitListener</code> to receive messages from the DLQ.
You could also use <code class="literal">RabbitTemplate.receive()</code> in a batch process.</p><p>The examples assume the original destination is <code class="literal">so8400in</code> and the consumer group is <code class="literal">so8400</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_non_partitioned_destinations" href="#_non_partitioned_destinations"></a>40.6.1&nbsp;Non-Partitioned Destinations</h3></div></div></div><p>The first two examples are for when the destination is <span class="strong"><strong>not</strong></span> partitioned:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ReRouteDlqApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String ORIGINAL_QUEUE = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"so8400in.so8400"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String DLQ = ORIGINAL_QUEUE + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".dlq"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String PARKING_LOT = ORIGINAL_QUEUE + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".parkingLot"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_RETRIES_HEADER = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-retries"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
ConfigurableApplicationContext context = SpringApplication.run(ReRouteDlqApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hit enter to terminate"</span>);
System.in.read();
context.close();
}
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RabbitTemplate rabbitTemplate;
<em><span class="hl-annotation" style="color: gray">@RabbitListener(queues = DLQ)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> rePublish(Message failedMessage) {
Integer retriesHeader = (Integer) failedMessage.getMessageProperties().getHeaders().get(X_RETRIES_HEADER);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retriesHeader == null) {
retriesHeader = Integer.valueOf(<span class="hl-number">0</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retriesHeader &lt; <span class="hl-number">3</span>) {
failedMessage.getMessageProperties().getHeaders().put(X_RETRIES_HEADER, retriesHeader + <span class="hl-number">1</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.rabbitTemplate.send(ORIGINAL_QUEUE, failedMessage);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.rabbitTemplate.send(PARKING_LOT, failedMessage);
}
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Queue parkingLot() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Queue(PARKING_LOT);
}
}</pre><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ReRouteDlqApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String ORIGINAL_QUEUE = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"so8400in.so8400"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String DLQ = ORIGINAL_QUEUE + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".dlq"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String PARKING_LOT = ORIGINAL_QUEUE + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".parkingLot"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_RETRIES_HEADER = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-retries"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String DELAY_EXCHANGE = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"dlqReRouter"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
ConfigurableApplicationContext context = SpringApplication.run(ReRouteDlqApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hit enter to terminate"</span>);
System.in.read();
context.close();
}
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RabbitTemplate rabbitTemplate;
<em><span class="hl-annotation" style="color: gray">@RabbitListener(queues = DLQ)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> rePublish(Message failedMessage) {
Map&lt;String, Object&gt; headers = failedMessage.getMessageProperties().getHeaders();
Integer retriesHeader = (Integer) headers.get(X_RETRIES_HEADER);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retriesHeader == null) {
retriesHeader = Integer.valueOf(<span class="hl-number">0</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retriesHeader &lt; <span class="hl-number">3</span>) {
headers.put(X_RETRIES_HEADER, retriesHeader + <span class="hl-number">1</span>);
headers.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-delay"</span>, <span class="hl-number">5000</span> * retriesHeader);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.rabbitTemplate.send(DELAY_EXCHANGE, ORIGINAL_QUEUE, failedMessage);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.rabbitTemplate.send(PARKING_LOT, failedMessage);
}
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> DirectExchange delayExchange() {
DirectExchange exchange = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> DirectExchange(DELAY_EXCHANGE);
exchange.setDelayed(true);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> exchange;
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Binding bindOriginalToDelay() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> BindingBuilder.bind(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Queue(ORIGINAL_QUEUE)).to(delayExchange()).with(ORIGINAL_QUEUE);
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Queue parkingLot() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Queue(PARKING_LOT);
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_partitioned_destinations" href="#_partitioned_destinations"></a>40.6.2&nbsp;Partitioned Destinations</h3></div></div></div><p>With partitioned destinations, there is one DLQ for all partitions. We determine the original queue from the headers.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="__literal_republishtodlq_false_literal" href="#__literal_republishtodlq_false_literal"></a><code class="literal">republishToDlq=false</code></h4></div></div></div><p>When <code class="literal">republishToDlq</code> is <code class="literal">false</code>, RabbitMQ publishes the message to the DLX/DLQ with an <code class="literal">x-death</code> header containing information about the original destination, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ReRouteDlqApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String ORIGINAL_QUEUE = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"so8400in.so8400"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String DLQ = ORIGINAL_QUEUE + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".dlq"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String PARKING_LOT = ORIGINAL_QUEUE + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".parkingLot"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_DEATH_HEADER = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-death"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_RETRIES_HEADER = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-retries"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
ConfigurableApplicationContext context = SpringApplication.run(ReRouteDlqApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hit enter to terminate"</span>);
System.in.read();
context.close();
}
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RabbitTemplate rabbitTemplate;
<em><span class="hl-annotation" style="color: gray">@SuppressWarnings("unchecked")</span></em>
<em><span class="hl-annotation" style="color: gray">@RabbitListener(queues = DLQ)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> rePublish(Message failedMessage) {
Map&lt;String, Object&gt; headers = failedMessage.getMessageProperties().getHeaders();
Integer retriesHeader = (Integer) headers.get(X_RETRIES_HEADER);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retriesHeader == null) {
retriesHeader = Integer.valueOf(<span class="hl-number">0</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retriesHeader &lt; <span class="hl-number">3</span>) {
headers.put(X_RETRIES_HEADER, retriesHeader + <span class="hl-number">1</span>);
List&lt;Map&lt;String, ?&gt;&gt; xDeath = (List&lt;Map&lt;String, ?&gt;&gt;) headers.get(X_DEATH_HEADER);
String exchange = (String) xDeath.get(<span class="hl-number">0</span>).get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"exchange"</span>);
List&lt;String&gt; routingKeys = (List&lt;String&gt;) xDeath.get(<span class="hl-number">0</span>).get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"routing-keys"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.rabbitTemplate.send(exchange, routingKeys.get(<span class="hl-number">0</span>), failedMessage);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.rabbitTemplate.send(PARKING_LOT, failedMessage);
}
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Queue parkingLot() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Queue(PARKING_LOT);
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="__literal_republishtodlq_true_literal" href="#__literal_republishtodlq_true_literal"></a><code class="literal">republishToDlq=true</code></h4></div></div></div><p>When <code class="literal">republishToDlq</code> is <code class="literal">true</code>, the republishing recoverer adds the original exchange and routing key to headers, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ReRouteDlqApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String ORIGINAL_QUEUE = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"so8400in.so8400"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String DLQ = ORIGINAL_QUEUE + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".dlq"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String PARKING_LOT = ORIGINAL_QUEUE + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".parkingLot"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_RETRIES_HEADER = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-retries"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_ORIGINAL_EXCHANGE_HEADER = RepublishMessageRecoverer.X_ORIGINAL_EXCHANGE;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_ORIGINAL_ROUTING_KEY_HEADER = RepublishMessageRecoverer.X_ORIGINAL_ROUTING_KEY;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
ConfigurableApplicationContext context = SpringApplication.run(ReRouteDlqApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hit enter to terminate"</span>);
System.in.read();
context.close();
}
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RabbitTemplate rabbitTemplate;
<em><span class="hl-annotation" style="color: gray">@RabbitListener(queues = DLQ)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> rePublish(Message failedMessage) {
Map&lt;String, Object&gt; headers = failedMessage.getMessageProperties().getHeaders();
Integer retriesHeader = (Integer) headers.get(X_RETRIES_HEADER);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retriesHeader == null) {
retriesHeader = Integer.valueOf(<span class="hl-number">0</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retriesHeader &lt; <span class="hl-number">3</span>) {
headers.put(X_RETRIES_HEADER, retriesHeader + <span class="hl-number">1</span>);
String exchange = (String) headers.get(X_ORIGINAL_EXCHANGE_HEADER);
String originalRoutingKey = (String) headers.get(X_ORIGINAL_ROUTING_KEY_HEADER);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.rabbitTemplate.send(exchange, originalRoutingKey, failedMessage);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.rabbitTemplate.send(PARKING_LOT, failedMessage);
}
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Queue parkingLot() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Queue(PARKING_LOT);
}
}</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_partitioning_with_the_rabbitmq_binder" href="#_partitioning_with_the_rabbitmq_binder"></a>40.7&nbsp;Partitioning with the RabbitMQ Binder</h2></div></div></div><p>RabbitMQ does not support partitioning natively.</p><p>Sometimes, it is advantageous to send data to specific partitions&#8201;&#8212;&#8201;for example, when you want to strictly order message processing, all messages for a particular customer should go to the same partition.</p><p>The <code class="literal">RabbitMessageChannelBinder</code> provides partitioning by binding a queue for each partition to the destination exchange.</p><p>The following Java and YAML examples show how to configure the producer:</p><p><b>Producer.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> RabbitPartitionProducerApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Random RANDOM = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Random(System.currentTimeMillis());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String[] data = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> String[] {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"abc1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"def1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux1"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"abc2"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"def2"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux2"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"abc3"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"def3"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux3"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"abc4"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"def4"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux4"</span>,
};
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(RabbitPartitionProducerApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
.web(false)
.run(args);
}
<em><span class="hl-annotation" style="color: gray">@InboundChannelAdapter(channel = Source.OUTPUT, poller = @Poller(fixedRate = "5000"))</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Message&lt;?&gt; generate() {
String value = data[RANDOM.nextInt(data.length)];
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Sending: "</span> + value);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> MessageBuilder.withPayload(value)
.setHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"partitionKey"</span>, value)
.build();
}
}</pre><p>
</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stream</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bindings</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> output</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: partitioned.destination
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> producer</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partitioned</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partition-key-expression</span>: headers[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'partitionKey'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partition-count</span>: <span class="hl-number">2</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> required-groups</span>:
- myGroup</pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The configuration in the prececing example uses the default partitioning (<code class="literal">key.hashCode() % partitionCount</code>).
This may or may not provide a suitably balanced algorithm, depending on the key values.
You can override this default by using the <code class="literal">partitionSelectorExpression</code> or <code class="literal">partitionSelectorClass</code> properties.</p><p>The <code class="literal">required-groups</code> property is required only if you need the consumer queues to be provisioned when the producer is deployed.
Otherwise, any messages sent to a partition are lost until the corresponding consumer is deployed.</p></td></tr></table></div><p>The following configuration provisions a topic exchange:</p><div class="informalfigure"><div class="mediaobject"><img src="images/part-exchange.png" alt="part exchange"></div></div><p>The following queues are bound to that exchange:</p><div class="informalfigure"><div class="mediaobject"><img src="images/part-queues.png" alt="part queues"></div></div><p>The following bindings associate the queues to the exchange:</p><div class="informalfigure"><div class="mediaobject"><img src="images/part-bindings.png" alt="part bindings"></div></div><p>The following Java and YAML examples continue the previous examples and show how to configure the consumer:</p><p><b>Consumer.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> RabbitPartitionConsumerApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(RabbitPartitionConsumerApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
.web(false)
.run(args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> listen(<em><span class="hl-annotation" style="color: gray">@Payload</span></em> String in, <em><span class="hl-annotation" style="color: gray">@Header(AmqpHeaders.CONSUMER_QUEUE)</span></em> String queue) {
System.out.println(in + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" received from queue "</span> + queue);
}
}</pre><p>
</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stream</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bindings</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> input</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: partitioned.destination
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> group</span>: myGroup
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> consumer</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partitioned</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> instance-index</span>: <span class="hl-number">0</span></pre><p>
</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">RabbitMessageChannelBinder</code> does not support dynamic scaling.
There must be at least one consumer per partition.
The consumer&#8217;s <code class="literal">instanceIndex</code> is used to indicate which partition is consumed.
Platforms such as Cloud Foundry can have only one instance with an <code class="literal">instanceIndex</code>.</p></td></tr></table></div></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_bus" href="#_spring_cloud_bus"></a>Part&nbsp;VII.&nbsp;Spring Cloud Bus</h1></div></div></div><div class="partintro"><div></div><p>Spring Cloud Bus links the nodes of a distributed system with a lightweight message
broker. This broker can then be used to broadcast state changes (such as configuration
changes) or other management instructions. A key idea is that the bus is like a
distributed actuator for a Spring Boot application that is scaled out. However, it can
also be used as a communication channel between apps. This project provides starters for
either an AMQP broker or Kafka as the transport.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Spring Cloud is released under the non-restrictive Apache 2.0 license. If you would like to contribute to this section of the documentation or if you find an error, please find the source code and issue trackers in the project at <a class="link" href="https://github.com/spring-cloud/spring-cloud-config/tree/master/docs/src/main/asciidoc" target="_top">github</a>.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_quick_start_3" href="#_quick_start_3"></a>41.&nbsp;Quick Start</h2></div></div></div><p>Spring Cloud Bus works by adding Spring Boot autconfiguration if it detects itself on the
classpath. To enable the bus, add <code class="literal">spring-cloud-starter-bus-amqp</code> or
<code class="literal">spring-cloud-starter-bus-kafka</code> to your dependency management. Spring Cloud takes care of
the rest. Make sure the broker (RabbitMQ or Kafka) is available and configured. When
running on localhost, you need not do anything. If you run remotely, use Spring Cloud
Connectors or Spring Boot conventions to define the broker credentials, as shown in the
following example for Rabbit:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring:
rabbitmq:
host: mybroker.com
port: 5672
username: user
password: secret</pre><p>
</p><p>The bus currently supports sending messages to all nodes listening or all nodes for a
particular service (as defined by Eureka). The <code class="literal">/bus/*</code> actuator namespace has some HTTP
endpoints. Currently, two are implemented. The first, <code class="literal">/bus/env</code>, sends key/value pairs to
update each node&#8217;s Spring Environment. The second, <code class="literal">/bus/refresh</code>, reloads each
application&#8217;s configuration, as though they had all been pinged on their <code class="literal">/refresh</code>
endpoint.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The Spring Cloud Bus starters cover Rabbit and Kafka, because those are the two most
common implementations. However, Spring Cloud Stream is quite flexible, and the binder
works with <code class="literal">spring-cloud-bus</code>.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_bus_endpoints" href="#_bus_endpoints"></a>42.&nbsp;Bus Endpoints</h2></div></div></div><p>Spring Cloud Bus provides two endpoints, <code class="literal">/actuator/bus-refresh</code> and <code class="literal">/actuator/bus-env</code>
that correspond to individual actuator endpoints in Spring Cloud Commons,
<code class="literal">/actuator/refresh</code> and <code class="literal">/actuator/env</code> respectively.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_bus_refresh_endpoint" href="#_bus_refresh_endpoint"></a>42.1&nbsp;Bus Refresh Endpoint</h2></div></div></div><p>The <code class="literal">/actuator/bus-refresh</code> endpoint clears the <code class="literal">RefreshScope</code> cache and rebinds
<code class="literal">@ConfigurationProperties</code>. See the <a class="link" href="#refresh-scope" title="2.9&nbsp;Refresh Scope">Refresh Scope</a> documentation for
more information.</p><p>To expose the <code class="literal">/actuator/bus-refresh</code> endpoint, you need to add following configuration to your
application:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">management.endpoints.web.exposure.include</span>=bus-refresh</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_bus_env_endpoint" href="#_bus_env_endpoint"></a>42.2&nbsp;Bus Env Endpoint</h2></div></div></div><p>The <code class="literal">/actuator/bus-env</code> endpoint updates each instances environment with the specified
key/value pair across multiple instances.</p><p>To expose the <code class="literal">/actuator/bus-env</code> endpoint, you need to add following configuration to your
application:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">management.endpoints.web.exposure.include</span>=bus-env</pre><p>The <code class="literal">/actuator/bus-env</code> endpoint accepts <code class="literal">POST</code> requests with the following shape:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"key1"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"value"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"value1"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_addressing_an_instance" href="#_addressing_an_instance"></a>43.&nbsp;Addressing an Instance</h2></div></div></div><p>Each instance of the application has a service ID, whose value can be set with
<code class="literal">spring.cloud.bus.id</code> and whose value is expected to be a colon-separated list of
identifiers, in order from least specific to most specific. The default value is
constructed from the environment as a combination of the <code class="literal">spring.application.name</code> and
<code class="literal">server.port</code> (or <code class="literal">spring.application.index</code>, if set). The default value of the ID is
constructed in the form of <code class="literal">app:index:id</code>, where:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">app</code> is the <code class="literal">vcap.application.name</code>, if it exists, or <code class="literal">spring.application.name</code></li><li class="listitem"><code class="literal">index</code> is the <code class="literal">vcap.application.instance_index</code>, if it exists,
<code class="literal">spring.application.index</code>, <code class="literal">local.server.port</code>, <code class="literal">server.port</code>, or <code class="literal">0</code> (in that order).</li><li class="listitem"><code class="literal">id</code> is the <code class="literal">vcap.application.instance_id</code>, if it exists, or a random value.</li></ul></div><p>The HTTP endpoints accept a &#8220;destination&#8221; path parameter, such as
<code class="literal">/bus-refresh/customers:9000</code>, where <code class="literal">destination</code> is a service ID. If the ID
is owned by an instance on the bus, it processes the message, and all other instances
ignore it.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_addressing_all_instances_of_a_service" href="#_addressing_all_instances_of_a_service"></a>44.&nbsp;Addressing All Instances of a Service</h2></div></div></div><p>The &#8220;destination&#8221; parameter is used in a Spring <code class="literal">PathMatcher</code> (with the path separator
as a colon&#8201;&#8212;&#8201;<code class="literal">:</code>) to determine if an instance processes the message. Using the example
from earlier, <code class="literal">/bus-env/customers:**</code> targets all instances of the
&#8220;customers&#8221; service regardless of the rest of the service ID.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_service_id_must_be_unique" href="#_service_id_must_be_unique"></a>45.&nbsp;Service ID Must Be Unique</h2></div></div></div><p>The bus tries twice to eliminate processing an event&#8201;&#8212;&#8201;once from the original
<code class="literal">ApplicationEvent</code> and once from the queue. To do so, it checks the sending service ID
against the current service ID. If multiple instances of a service have the same ID,
events are not processed. When running on a local machine, each service is on a different
port, and that port is part of the ID. Cloud Foundry supplies an index to differentiate.
To ensure that the ID is unique outside Cloud Foundry, set <code class="literal">spring.application.index</code> to
something unique for each instance of a service.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_customizing_the_message_broker" href="#_customizing_the_message_broker"></a>46.&nbsp;Customizing the Message Broker</h2></div></div></div><p>Spring Cloud Bus uses <a class="link" href="https://cloud.spring.io/spring-cloud-stream" target="_top">Spring Cloud Stream</a> to
broadcast the messages. So, to get messages to flow, you need only include the binder
implementation of your choice in the classpath. There are convenient starters for the bus
with AMQP (RabbitMQ) and Kafka (<code class="literal">spring-cloud-starter-bus-[amqp|kafka]</code>). Generally
speaking, Spring Cloud Stream relies on Spring Boot autoconfiguration conventions for
configuring middleware. For instance, the AMQP broker address can be changed with
<code class="literal">spring.rabbitmq.*</code> configuration properties. Spring Cloud Bus has a handful of
native configuration properties in <code class="literal">spring.cloud.bus.*</code> (for example,
<code class="literal">spring.cloud.bus.destination</code> is the name of the topic to use as the external
middleware). Normally, the defaults suffice.</p><p>To learn more about how to customize the message broker settings, consult the Spring Cloud
Stream documentation.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_tracing_bus_events" href="#_tracing_bus_events"></a>47.&nbsp;Tracing Bus Events</h2></div></div></div><p>Bus events (subclasses of <code class="literal">RemoteApplicationEvent</code>) can be traced by setting
<code class="literal">spring.cloud.bus.trace.enabled=true</code>. If you do so, the Spring Boot <code class="literal">TraceRepository</code>
(if it is present) shows each event sent and all the acks from each service instance. The
following example comes from the <code class="literal">/trace</code> endpoint:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"timestamp"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2015-11-26T10:24:44.411+0000"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"info"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"signal"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spring.cloud.bus.ack"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"RefreshRemoteApplicationEvent"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"c4d374b7-58ea-4928-a312-31984def293b"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"origin"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stores:8081"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"destination"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"*:**"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"timestamp"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2015-11-26T10:24:41.864+0000"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"info"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"signal"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spring.cloud.bus.sent"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"RefreshRemoteApplicationEvent"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"c4d374b7-58ea-4928-a312-31984def293b"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"origin"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"customers:9000"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"destination"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"*:**"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"timestamp"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2015-11-26T10:24:41.862+0000"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"info"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"signal"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spring.cloud.bus.ack"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"RefreshRemoteApplicationEvent"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"c4d374b7-58ea-4928-a312-31984def293b"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"origin"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"customers:9000"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"destination"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"*:**"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>The preceding trace shows that a <code class="literal">RefreshRemoteApplicationEvent</code> was sent from
<code class="literal">customers:9000</code>, broadcast to all services, and received (acked) by <code class="literal">customers:9000</code> and
<code class="literal">stores:8081</code>.</p><p>To handle the ack signals yourself, you could add an <code class="literal">@EventListener</code> for the
<code class="literal">AckRemoteApplicationEvent</code> and <code class="literal">SentApplicationEvent</code> types to your app (and enable
tracing). Alternatively, you could tap into the <code class="literal">TraceRepository</code> and mine the data from
there.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Any Bus application can trace acks. However, sometimes, it is
useful to do this in a central service that can do more complex
queries on the data or forward it to a specialized tracing service.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_broadcasting_your_own_events" href="#_broadcasting_your_own_events"></a>48.&nbsp;Broadcasting Your Own Events</h2></div></div></div><p>The Bus can carry any event of type <code class="literal">RemoteApplicationEvent</code>. The default transport is
JSON, and the deserializer needs to know which types are going to be used ahead of time.
To register a new type, you must put it in a subpackage of
<code class="literal">org.springframework.cloud.bus.event</code>.</p><p>To customise the event name, you can use <code class="literal">@JsonTypeName</code> on your custom class or rely on
the default strategy, which is to use the simple name of the class.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Both the producer and the consumer need access to the class definition.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_events_in_custom_packages" href="#_registering_events_in_custom_packages"></a>48.1&nbsp;Registering events in custom packages</h2></div></div></div><p>If you cannot or do not want to use a subpackage of <code class="literal">org.springframework.cloud.bus.event</code>
for your custom events, you must specify which packages to scan for events of type
<code class="literal">RemoteApplicationEvent</code> by using the <code class="literal">@RemoteApplicationEventScan</code> annotation. Packages
specified with <code class="literal">@RemoteApplicationEventScan</code> include subpackages.</p><p>For example, consider the following custom event, called <code class="literal">MyEvent</code>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.acme;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyEvent <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> RemoteApplicationEvent {
...
}</pre><p>You can register that event with the deserializer in the following way:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.acme;
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@RemoteApplicationEventScan</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BusConfiguration {
...
}</pre><p>Without specifying a value, the package of the class where <code class="literal">@RemoteApplicationEventScan</code>
is used is registered. In this example, <code class="literal">com.acme</code> is registered by using the package of
<code class="literal">BusConfiguration</code>.</p><p>You can also explicitly specify the packages to scan by using the <code class="literal">value</code>, <code class="literal">basePackages</code>
or <code class="literal">basePackageClasses</code> properties on <code class="literal">@RemoteApplicationEventScan</code>, as shown in the
following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.acme;
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//@RemoteApplicationEventScan({"com.acme", "foo.bar"})</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//@RemoteApplicationEventScan(basePackages = {"com.acme", "foo.bar", "fizz.buzz"})</span>
<em><span class="hl-annotation" style="color: gray">@RemoteApplicationEventScan(basePackageClasses = BusConfiguration.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BusConfiguration {
...
}</pre><p>All of the preceding examples of <code class="literal">@RemoteApplicationEventScan</code> are equivalent, in that the
<code class="literal">com.acme</code> package is registered by explicitly specifying the packages on
<code class="literal">@RemoteApplicationEventScan</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>You can specify multiple base packages to scan.</p></td></tr></table></div></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_sleuth" href="#_spring_cloud_sleuth"></a>Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth</h1></div></div></div><div class="partintro"><div></div><p>Adrian Cole, Spencer Gibb, Marcin Grzejszczak, Dave Syer, Jay Bryant</p><p><span class="strong"><strong>Greenwich.M3</strong></span></p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_introduction" href="#_introduction"></a>49.&nbsp;Introduction</h2></div></div></div><p>Spring Cloud Sleuth implements a distributed tracing solution for <a class="link" href="http://cloud.spring.io" target="_top">Spring Cloud</a>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_terminology" href="#_terminology"></a>49.1&nbsp;Terminology</h2></div></div></div><p>Spring Cloud Sleuth borrows <a class="link" href="http://research.google.com/pubs/pub36356.html" target="_top">Dapper&#8217;s</a> terminology.</p><p><span class="strong"><strong>Span</strong></span>: The basic unit of work. For example, sending an RPC is a new span, as is sending a response to an RPC.
Spans are identified by a unique 64-bit ID for the span and another 64-bit ID for the trace the span is a part of.
Spans also have other data, such as descriptions, timestamped events, key-value annotations (tags), the ID of the span that caused them, and process IDs (normally IP addresses).</p><p>Spans can be started and stopped, and they keep track of their timing information.
Once you create a span, you must stop it at some point in the future.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>The initial span that starts a trace is called a <code class="literal">root span</code>. The value of the ID
of that span is equal to the trace ID.</p></td></tr></table></div><p><span class="strong"><strong>Trace:</strong></span> A set of spans forming a tree-like structure.
For example, if you run a distributed big-data store, a trace might be formed by a <code class="literal">PUT</code> request.</p><p><span class="strong"><strong>Annotation:</strong></span> Used to record the existence of an event in time. With
<a class="link" href="https://github.com/openzipkin/brave" target="_top">Brave</a> instrumentation, we no longer need to set special events
for <a class="link" href="https://zipkin.io/" target="_top">Zipkin</a> to understand who the client and server are, where
the request started, and where it ended. For learning purposes,
however, we mark these events to highlight what kind
of an action took place.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>cs</strong></span>: Client Sent. The client has made a request. This annotation indicates the start of the span.</li><li class="listitem"><span class="strong"><strong>sr</strong></span>: Server Received: The server side got the request and started processing it.
Subtracting the <code class="literal">cs</code> timestamp from this timestamp reveals the network latency.</li><li class="listitem"><span class="strong"><strong>ss</strong></span>: Server Sent. Annotated upon completion of request processing (when the response got sent back to the client).
Subtracting the <code class="literal">sr</code> timestamp from this timestamp reveals the time needed by the server side to process the request.</li><li class="listitem"><span class="strong"><strong>cr</strong></span>: Client Received. Signifies the end of the span.
The client has successfully received the response from the server side.
Subtracting the <code class="literal">cs</code> timestamp from this timestamp reveals the whole time needed by the client to receive the response from the server.</li></ul></div><p>The following image shows how <span class="strong"><strong>Span</strong></span> and <span class="strong"><strong>Trace</strong></span> look in a system, together with the Zipkin annotations:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/trace-id.png" alt="Trace Info propagation"></div></div><p>Each color of a note signifies a span (there are seven spans - from <span class="strong"><strong>A</strong></span> to <span class="strong"><strong>G</strong></span>).
Consider the following note:</p><pre class="screen">Trace Id = X
Span Id = D
Client Sent</pre><p>This note indicates that the current span has <span class="strong"><strong>Trace Id</strong></span> set to <span class="strong"><strong>X</strong></span> and <span class="strong"><strong>Span Id</strong></span> set to <span class="strong"><strong>D</strong></span>.
Also, the <code class="literal">Client Sent</code> event took place.</p><p>The following image shows how parent-child relationships of spans look:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/parents.png" alt="Parent child relationship"></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_purpose" href="#_purpose"></a>49.2&nbsp;Purpose</h2></div></div></div><p>The following sections refer to the example shown in the preceding image.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_distributed_tracing_with_zipkin" href="#_distributed_tracing_with_zipkin"></a>49.2.1&nbsp;Distributed Tracing with Zipkin</h3></div></div></div><p>This example has seven spans.
If you go to traces in Zipkin, you can see this number in the second trace, as shown in the following image:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-traces.png" alt="Traces"></div></div><p>However, if you pick a particular trace, you can see four spans, as shown in the following image:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-ui.png" alt="Traces Info propagation"></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When you pick a particular trace, you see merged spans.
That means that, if there were two spans sent to Zipkin with Server Received and Server Sent or Client Received and Client Sent annotations, they are presented as a single span.</p></td></tr></table></div><p>Why is there a difference between the seven and four spans in this case?</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Two spans come from the <code class="literal">http:/start</code> span. It has the Server Received (<code class="literal">sr</code>) and Server Sent (<code class="literal">ss</code>) annotations.</li><li class="listitem">Two spans come from the RPC call from <code class="literal">service1</code> to <code class="literal">service2</code> to the <code class="literal">http:/foo</code> endpoint.
The Client Sent (<code class="literal">cs</code>) and Client Received (<code class="literal">cr</code>) events took place on the <code class="literal">service1</code> side.
Server Received (<code class="literal">sr</code>) and Server Sent (<code class="literal">ss</code>) events took place on the <code class="literal">service2</code> side.
These two spans form one logical span related to an RPC call.</li><li class="listitem">Two spans come from the RPC call from <code class="literal">service2</code> to <code class="literal">service3</code> to the <code class="literal">http:/bar</code> endpoint.
The Client Sent (<code class="literal">cs</code>) and Client Received (<code class="literal">cr</code>) events took place on the <code class="literal">service2</code> side.
The Server Received (<code class="literal">sr</code>) and Server Sent (<code class="literal">ss</code>) events took place on the <code class="literal">service3</code> side.
These two spans form one logical span related to an RPC call.</li><li class="listitem">Two spans come from the RPC call from <code class="literal">service2</code> to <code class="literal">service4</code> to the <code class="literal">http:/baz</code> endpoint.
The Client Sent (<code class="literal">cs</code>) and Client Received (<code class="literal">cr</code>) events took place on the <code class="literal">service2</code> side.
Server Received (<code class="literal">sr</code>) and Server Sent (<code class="literal">ss</code>) events took place on the <code class="literal">service4</code> side.
These two spans form one logical span related to an RPC call.</li></ul></div><p>So, if we count the physical spans, we have one from <code class="literal">http:/start</code>, two from <code class="literal">service1</code> calling <code class="literal">service2</code>, two from <code class="literal">service2</code>
calling <code class="literal">service3</code>, and two from <code class="literal">service2</code> calling <code class="literal">service4</code>. In sum, we have a total of seven spans.</p><p>Logically, we see the information of four total Spans because we have one span related to the incoming request
to <code class="literal">service1</code> and three spans related to RPC calls.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_visualizing_errors" href="#_visualizing_errors"></a>49.2.2&nbsp;Visualizing errors</h3></div></div></div><p>Zipkin lets you visualize errors in your trace.
When an exception was thrown and was not caught, we set proper tags on the span, which Zipkin can then properly colorize.
You could see in the list of traces one trace that is red. That appears because an exception was thrown.</p><p>If you click that trace, you see a similar picture, as follows:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-error-traces.png" alt="Error Traces"></div></div><p>If you then click on one of the spans, you see the following</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/zipkin-error-trace-screenshot.png" alt="Error Traces Info propagation"></div></div><p>The span shows the reason for the error and the whole stack trace related to it.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_distributed_tracing_with_brave" href="#_distributed_tracing_with_brave"></a>49.2.3&nbsp;Distributed Tracing with Brave</h3></div></div></div><p>Starting with version <code class="literal">2.0.0</code>, Spring Cloud Sleuth uses <a class="link" href="https://github.com/openzipkin/brave" target="_top">Brave</a> as the tracing library.
Consequently, Sleuth no longer takes care of storing the context but delegates that work to Brave.</p><p>Due to the fact that Sleuth had different naming and tagging conventions than Brave, we decided to follow Brave&#8217;s conventions from now on.
However, if you want to use the legacy Sleuth approaches, you can set the <code class="literal">spring.sleuth.http.legacy.enabled</code> property to <code class="literal">true</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_live_examples" href="#_live_examples"></a>49.2.4&nbsp;Live examples</h3></div></div></div><div class="figure"><a name="d0e15956" href="#d0e15956"></a><p class="title"><b>Figure&nbsp;49.1.&nbsp;Click the Pivotal Web Services icon to see it live!</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/pws.png" alt="Zipkin deployed on Pivotal Web Services"></div></div></div><br class="figure-break"><p><a class="link" href="http://docssleuth-zipkin-server.cfapps.io/" target="_top">Click here to see it live!</a></p><p>The dependency graph in Zipkin should resemble the following image:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/dependencies.png" alt="Dependencies"></div></div><div class="figure"><a name="d0e15977" href="#d0e15977"></a><p class="title"><b>Figure&nbsp;49.2.&nbsp;Click the Pivotal Web Services icon to see it live!</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/pws.png" alt="Zipkin deployed on Pivotal Web Services"></div></div></div><br class="figure-break"><p><a class="link" href="http://docssleuth-zipkin-server.cfapps.io/dependency" target="_top">Click here to see it live!</a></p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_log_correlation" href="#_log_correlation"></a>49.2.5&nbsp;Log correlation</h3></div></div></div><p>When using grep to read the logs of those four applications by scanning for a trace ID equal to (for example) <code class="literal">2485ec27856c56f4</code>, you get output resembling the following:</p><pre class="screen">service1.log:2016-02-26 11:15:47.561 INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application : Hello from service1. Calling service2
service2.log:2016-02-26 11:15:47.710 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Hello from service2. Calling service3 and then service4
service3.log:2016-02-26 11:15:47.895 INFO [service3,2485ec27856c56f4,1210be13194bfe5,true] 68060 --- [nio-8083-exec-1] i.s.c.sleuth.docs.service3.Application : Hello from service3
service2.log:2016-02-26 11:15:47.924 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Got response from service3 [Hello from service3]
service4.log:2016-02-26 11:15:48.134 INFO [service4,2485ec27856c56f4,1b1845262ffba49d,true] 68061 --- [nio-8084-exec-1] i.s.c.sleuth.docs.service4.Application : Hello from service4
service2.log:2016-02-26 11:15:48.156 INFO [service2,2485ec27856c56f4,9aa10ee6fbde75fa,true] 68059 --- [nio-8082-exec-1] i.s.c.sleuth.docs.service2.Application : Got response from service4 [Hello from service4]
service1.log:2016-02-26 11:15:48.182 INFO [service1,2485ec27856c56f4,2485ec27856c56f4,true] 68058 --- [nio-8081-exec-1] i.s.c.sleuth.docs.service1.Application : Got response from service2 [Hello from service2, response from service3 [Hello from service3] and from service4 [Hello from service4]]</pre><p>If you use a log aggregating tool (such as <a class="link" href="https://www.elastic.co/products/kibana" target="_top">Kibana</a>, <a class="link" href="http://www.splunk.com/" target="_top">Splunk</a>, and others), you can order the events that took place.
An example from Kibana would resemble the following image:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-sleuth/master/docs/src/main/asciidoc/images/kibana.png" alt="Log correlation with Kibana"></div></div><p>If you want to use <a class="link" href="https://www.elastic.co/guide/en/logstash/current/index.html" target="_top">Logstash</a>, the following listing shows the Grok pattern for Logstash:</p><pre class="screen">filter {
# pattern matching logback pattern
grok {
match =&gt; { "message" =&gt; "%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
}
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you want to use Grok together with the logs from Cloud Foundry, you have to use the following pattern:</p></td></tr></table></div><pre class="screen">filter {
# pattern matching logback pattern
grok {
match =&gt; { "message" =&gt; "(?m)OUT\s+%{TIMESTAMP_ISO8601:timestamp}\s+%{LOGLEVEL:severity}\s+\[%{DATA:service},%{DATA:trace},%{DATA:span},%{DATA:exportable}\]\s+%{DATA:pid}\s+---\s+\[%{DATA:thread}\]\s+%{DATA:class}\s+:\s+%{GREEDYDATA:rest}" }
}
}</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_json_logback_with_logstash" href="#_json_logback_with_logstash"></a>JSON Logback with Logstash</h4></div></div></div><p>Often, you do not want to store your logs in a text file but in a JSON file that Logstash can immediately pick.
To do so, you have to do the following (for readability, we pass the dependencies in the <code class="literal">groupId:artifactId:version</code> notation).</p><p><span class="strong"><strong>Dependencies Setup</strong></span></p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Ensure that Logback is on the classpath (<code class="literal">ch.qos.logback:logback-core</code>).</li><li class="listitem">Add Logstash Logback encode. For example, to use version <code class="literal">4.6</code>, add <code class="literal">net.logstash.logback:logstash-logback-encoder:4.6</code>.</li></ol></div><p><span class="strong"><strong>Logback Setup</strong></span></p><p>Consider the following example of a Logback configuration file (named <a class="link" href="https://github.com/spring-cloud-samples/sleuth-documentation-apps/blob/master/service1/src/main/resources/logback-spring.xml" target="_top">logback-spring.xml</a>).</p><pre class="programlisting"><span class="hl-directive" style="color: maroon">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">resource</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"org/springframework/boot/logging/logback/defaults.xml"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/&gt;</span>
&#8203;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;springProperty</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">scope</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"context"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"springAppName"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">source</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"spring.application.name"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Example for logging into the build folder of your project --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;property</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"LOG_FILE"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">value</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"${BUILD_FOLDER:-build}/${springAppName}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/&gt;</span>&#8203;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- You can override this to have a custom pattern --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;property</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"CONSOLE_LOG_PATTERN"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">value</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Appender to log to console --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appender</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"console"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.ConsoleAppender"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;filter</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.classic.filter.ThresholdFilter"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Minimum logging level to be presented in the console logs--&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;level&gt;</span>DEBUG<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/level&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/filter&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;encoder&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pattern&gt;</span>${CONSOLE_LOG_PATTERN}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pattern&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;charset&gt;</span>utf8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/charset&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/encoder&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/appender&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Appender to log to file --&gt;</span>&#8203;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appender</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"flatfile"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.rolling.RollingFileAppender"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;file&gt;</span>${LOG_FILE}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/file&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;rollingPolicy</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileNamePattern&gt;</span>${LOG_FILE}.%d{yyyy-MM-dd}.gz<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileNamePattern&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;maxHistory&gt;</span>7<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/maxHistory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/rollingPolicy&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;encoder&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pattern&gt;</span>${CONSOLE_LOG_PATTERN}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pattern&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;charset&gt;</span>utf8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/charset&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/encoder&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/appender&gt;</span>
&#8203;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Appender to log to file in a JSON format --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appender</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"logstash"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.rolling.RollingFileAppender"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;file&gt;</span>${LOG_FILE}.json<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/file&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;rollingPolicy</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.rolling.TimeBasedRollingPolicy"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileNamePattern&gt;</span>${LOG_FILE}.json.%d{yyyy-MM-dd}.gz<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileNamePattern&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;maxHistory&gt;</span>7<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/maxHistory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/rollingPolicy&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;encoder</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;providers&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;timestamp&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;timeZone&gt;</span>UTC<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/timeZone&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/timestamp&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pattern&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pattern&gt;</span>
{
"severity": "%level",
"service": "${springAppName:-}",
"trace": "%X{X-B3-TraceId:-}",
"span": "%X{X-B3-SpanId:-}",
"parent": "%X{X-B3-ParentSpanId:-}",
"exportable": "%X{X-Span-Export:-}",
"pid": "${PID:-}",
"thread": "%thread",
"class": "%logger{40}",
"rest": "%message"
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pattern&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pattern&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/providers&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/encoder&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/appender&gt;</span>
&#8203;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;root</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">level</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"INFO"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appender-ref</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">ref</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"console"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- uncomment this to have also JSON logs --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!--&lt;appender-ref ref="logstash"/&gt;--&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!--&lt;appender-ref ref="flatfile"/&gt;--&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/root&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span></pre><p>That Logback configuration file:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Logs information from the application in a JSON format to a <code class="literal">build/${spring.application.name}.json</code> file.</li><li class="listitem">Has commented out two additional appenders: console and standard log file.</li><li class="listitem">Has the same logging pattern as the one presented in the previous section.</li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you use a custom <code class="literal">logback-spring.xml</code>, you must pass the <code class="literal">spring.application.name</code> in the <code class="literal">bootstrap</code> rather than the <code class="literal">application</code> property file.
Otherwise, your custom logback file does not properly read the property.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_propagating_span_context" href="#_propagating_span_context"></a>49.2.6&nbsp;Propagating Span Context</h3></div></div></div><p>The span context is the state that must get propagated to any child spans across process boundaries.
Part of the Span Context is the Baggage. The trace and span IDs are a required part of the span context.
Baggage is an optional part.</p><p>Baggage is a set of key:value pairs stored in the span context.
Baggage travels together with the trace and is attached to every span.
Spring Cloud Sleuth understands that a header is baggage-related if the HTTP header is prefixed with <code class="literal">baggage-</code> and, for messaging, it starts with <code class="literal">baggage_</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>There is currently no limitation of the count or size of baggage items.
However, keep in mind that too many can decrease system throughput or increase RPC latency.
In extreme cases, too much baggage can crash the application, due to exceeding transport-level message or header capacity.</p></td></tr></table></div><p>The following example shows setting baggage on a span:</p><pre class="programlisting">Span initialSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"span"</span>).start();
ExtraFieldPropagation.set(initialSpan.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>);
ExtraFieldPropagation.set(initialSpan.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UPPER_CASE"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"someValue"</span>);
}</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_baggage_versus_span_tags" href="#_baggage_versus_span_tags"></a>Baggage versus Span Tags</h4></div></div></div><p>Baggage travels with the trace (every child span contains the baggage of its parent).
Zipkin has no knowledge of baggage and does not receive that information.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting from Sleuth 2.0.0 you have to pass the baggage key names explicitly
in your project configuration. Read more about that setup <a class="link" href="#prefixed-fields" title="53.1.1&nbsp;Prefixed fields">here</a></p></td></tr></table></div><p>Tags are attached to a specific span. In other words, they are presented only for that particular span.
However, you can search by tag to find the trace, assuming a span having the searched tag value exists.</p><p>If you want to be able to lookup a span based on baggage, you should add a corresponding entry as a tag in the root span.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The span must be in scope.</p></td></tr></table></div><p>The following listing shows integration tests that use baggage:</p><p><b>The setup.&nbsp;</b>
</p><pre class="programlisting">spring.sleuth:
baggage-keys:
- baz
- bizarrecase
propagation-keys:
- foo
- upper_case</pre><p>
</p><p><b>The code.&nbsp;</b>
</p><pre class="programlisting">initialSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>,
ExtraFieldPropagation.get(initialSpan.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>));
initialSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UPPER_CASE"</span>,
ExtraFieldPropagation.get(initialSpan.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UPPER_CASE"</span>));</pre><p>
</p></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="sleuth-adding-project" href="#sleuth-adding-project"></a>49.3&nbsp;Adding Sleuth to the Project</h2></div></div></div><p>This section addresses how to add Sleuth to your project with either Maven or Gradle.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>To ensure that your application name is properly displayed in Zipkin, set the <code class="literal">spring.application.name</code> property in <code class="literal">bootstrap.yml</code>.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_only_sleuth_log_correlation" href="#_only_sleuth_log_correlation"></a>49.3.1&nbsp;Only Sleuth (log correlation)</h3></div></div></div><p>If you want to use only Spring Cloud Sleuth without the Zipkin integration, add the <code class="literal">spring-cloud-starter-sleuth</code> module to your project.</p><p>The following example shows how to add Sleuth with Maven:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span> <a name="CO1-1" href="#CO1-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${release.train.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span> <a name="CO1-2" href="#CO1-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-sleuth<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO1-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO1-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-sleuth</code>.</p></td></tr></table></div><p>The following example shows how to add Sleuth with Gradle:</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">dependencyManagement { <a name="CO2-1" href="#CO2-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
imports {
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"</span>
}
}
dependencies { <a name="CO2-2" href="#CO2-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-starter-sleuth"</span>
}</pre><p class="secondary">
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO2-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO2-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-sleuth</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_sleuth_with_zipkin_via_http" href="#_sleuth_with_zipkin_via_http"></a>49.3.2&nbsp;Sleuth with Zipkin via HTTP</h3></div></div></div><p>If you want both Sleuth and Zipkin, add the <code class="literal">spring-cloud-starter-zipkin</code> dependency.</p><p>The following example shows how to do so for Maven:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span> <a name="CO3-1" href="#CO3-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${release.train.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span> <a name="CO3-2" href="#CO3-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-zipkin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO3-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO3-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-zipkin</code>.</p></td></tr></table></div><p>The following example shows how to do so for Gradle:</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">dependencyManagement { <a name="CO4-1" href="#CO4-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
imports {
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"</span>
}
}
dependencies { <a name="CO4-2" href="#CO4-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-starter-zipkin"</span>
}</pre><p class="secondary">
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO4-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO4-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-zipkin</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_sleuth_with_zipkin_over_rabbitmq_or_kafka" href="#_sleuth_with_zipkin_over_rabbitmq_or_kafka"></a>49.3.3&nbsp;Sleuth with Zipkin over RabbitMQ or Kafka</h3></div></div></div><p>If you want to use RabbitMQ or Kafka instead of HTTP, add the <code class="literal">spring-rabbit</code> or <code class="literal">spring-kafka</code> dependency.
The default destination name is <code class="literal">zipkin</code>.</p><p>If using Kafka, you must set the property <code class="literal">spring.zipkin.sender.type</code> property accordingly:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.sender.type</span>: kafka</pre><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p><code class="literal">spring-cloud-sleuth-stream</code> is deprecated and incompatible with these destinations.</p></td></tr></table></div><p>If you want Sleuth over RabbitMQ, add the <code class="literal">spring-cloud-starter-zipkin</code> and <code class="literal">spring-rabbit</code>
dependencies.</p><p>The following example shows how to do so for Gradle:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span> <a name="CO5-1" href="#CO5-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${release.train.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span> <a name="CO5-2" href="#CO5-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-zipkin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span> <a name="CO5-3" href="#CO5-3"></a><span><img src="images/callouts/3.png" alt="3" border="0"></span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.amqp<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-rabbit<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO5-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO5-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-zipkin</code>. That way, all nested dependencies get downloaded.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO5-3"><span><img src="images/callouts/3.png" alt="3" border="0"></span></a> </p></td><td valign="top" align="left"><p>To automatically configure RabbitMQ, add the <code class="literal">spring-rabbit</code> dependency.</p></td></tr></table></div><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">dependencyManagement { <a name="CO6-1" href="#CO6-1"></a><span><img src="images/callouts/1.png" alt="1" border="0"></span>
imports {
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-dependencies:${releaseTrainVersion}"</span>
}
}
dependencies {
compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-starter-zipkin"</span> <a name="CO6-2" href="#CO6-2"></a><span><img src="images/callouts/2.png" alt="2" border="0"></span>
compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.amqp:spring-rabbit"</span> <a name="CO6-3" href="#CO6-3"></a><span><img src="images/callouts/3.png" alt="3" border="0"></span>
}</pre><p class="secondary">
</p><div class="calloutlist"><table border="0" summary="Callout list"><tr><td width="5%" valign="top" align="left"><p><a href="#CO6-1"><span><img src="images/callouts/1.png" alt="1" border="0"></span></a> </p></td><td valign="top" align="left"><p>We recommend that you add the dependency management through the Spring BOM so that you need not manage versions yourself.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO6-2"><span><img src="images/callouts/2.png" alt="2" border="0"></span></a> </p></td><td valign="top" align="left"><p>Add the dependency to <code class="literal">spring-cloud-starter-zipkin</code>. That way, all nested dependencies get downloaded.</p></td></tr><tr><td width="5%" valign="top" align="left"><p><a href="#CO6-3"><span><img src="images/callouts/3.png" alt="3" border="0"></span></a> </p></td><td valign="top" align="left"><p>To automatically configure RabbitMQ, add the <code class="literal">spring-rabbit</code> dependency.</p></td></tr></table></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_additional_resources" href="#_additional_resources"></a>50.&nbsp;Additional Resources</h2></div></div></div><p>You can watch a video of <a class="link" href="https://twitter.com/reshmi9k" target="_top">Reshmi Krishna</a> and <a class="link" href="https://twitter.com/mgrzejszczak" target="_top">Marcin Grzejszczak</a> talking about Spring Cloud
Sleuth and Zipkin <a class="link" href="https://content.pivotal.io/springone-platform-2017/distributed-tracing-latency-analysis-for-your-microservices-grzejszczak-krishna" target="_top">by clicking here</a>.</p><p>You can check different setups of Sleuth and Brave <a class="link" href="https://github.com/openzipkin/sleuth-webmvc-example" target="_top">in the openzipkin/sleuth-webmvc-example repository</a>.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_features_2" href="#_features_2"></a>51.&nbsp;Features</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">Adds trace and span IDs to the Slf4J MDC, so you can extract all the logs from a given trace or span in a log aggregator, as shown in the following example logs:</p><pre class="screen">2016-02-02 15:30:57.902 INFO [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ...
2016-02-02 15:30:58.372 ERROR [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ...
2016-02-02 15:31:01.936 INFO [bar,46ab0d418373cbc9,46ab0d418373cbc9,false] 23030 --- [nio-8081-exec-4] ...</pre><p class="simpara">Notice the <code class="literal">[appname,traceId,spanId,exportable]</code> entries from the MDC:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><span class="strong"><strong><code class="literal">spanId</code></strong></span>: The ID of a specific operation that took place.</li><li class="listitem"><span class="strong"><strong><code class="literal">appname</code></strong></span>: The name of the application that logged the span.</li><li class="listitem"><span class="strong"><strong><code class="literal">traceId</code></strong></span>: The ID of the latency graph that contains the span.</li><li class="listitem"><span class="strong"><strong><code class="literal">exportable</code></strong></span>: Whether the log should be exported to Zipkin.
When would you like the span not to be exportable?
When you want to wrap some operation in a Span and have it written to the logs only.</li></ul></div></li><li class="listitem">Provides an abstraction over common distributed tracing data models: traces, spans (forming a DAG), annotations, and key-value annotations.
Spring Cloud Sleuth is loosely based on HTrace but is compatible with Zipkin (Dapper).</li><li class="listitem">Sleuth records timing information to aid in latency analysis.
By using sleuth, you can pinpoint causes of latency in your applications.</li><li class="listitem"><p class="simpara">Sleuth is written to not log too much and to not cause your production application to crash.
To that end, Sleuth:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">Propagates structural data about your call graph in-band and the rest out-of-band.</li><li class="listitem">Includes opinionated instrumentation of layers such as HTTP.</li><li class="listitem">Includes a sampling policy to manage volume.</li><li class="listitem">Can report to a Zipkin system for query and visualization.</li></ul></div></li><li class="listitem">Instruments common ingress and egress points from Spring applications (servlet filter, async endpoints, rest template, scheduled actions, message channels, Zuul filters, and Feign client).</li><li class="listitem">Sleuth includes default logic to join a trace across HTTP or messaging boundaries.
For example, HTTP propagation works over Zipkin-compatible request headers.</li><li class="listitem">Sleuth can propagate context (also known as baggage) between processes.
Consequently, if you set a baggage element on a Span, it is sent downstream to other processes over either HTTP or messaging.</li><li class="listitem">Provides a way to create or continue spans and add tags and logs through annotations.</li><li class="listitem"><p class="simpara">If <code class="literal">spring-cloud-sleuth-zipkin</code> is on the classpath, the app generates and collects Zipkin-compatible traces.
By default, it sends them over HTTP to a Zipkin server on localhost (port 9411).
You can configure the location of the service by setting <code class="literal">spring.zipkin.baseUrl</code>.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">If you depend on <code class="literal">spring-rabbit</code>, your app sends traces to a RabbitMQ broker instead of HTTP.</li><li class="listitem">If you depend on <code class="literal">spring-kafka</code>, and set <code class="literal">spring.zipkin.sender.type: kafka</code>, your app sends traces to a Kafka broker instead of HTTP.</li></ul></div></li></ul></div><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p><code class="literal">spring-cloud-sleuth-stream</code> is deprecated and should no longer be used.</p></td></tr></table></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Spring Cloud Sleuth is <a class="link" href="http://opentracing.io/" target="_top">OpenTracing</a> compatible.</li></ul></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you use Zipkin, configure the probability of spans exported by setting <code class="literal">spring.sleuth.sampler.probability</code>
(default: 0.1, which is 10 percent). Otherwise, you might think that Sleuth is not working be cause it omits some spans.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The SLF4J MDC is always set and logback users immediately see the trace and span IDs in logs per the example
shown earlier.
Other logging systems have to configure their own formatter to get the same result.
The default is as follows:
<code class="literal">logging.pattern.level</code> set to <code class="literal">%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]</code>
(this is a Spring Boot feature for logback users).
If you do not use SLF4J, this pattern is NOT automatically applied.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_introduction_to_brave" href="#_introduction_to_brave"></a>51.1&nbsp;Introduction to Brave</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting with version <code class="literal">2.0.0</code>, Spring Cloud Sleuth uses
<a class="link" href="https://github.com/openzipkin/brave" target="_top">Brave</a> as the tracing library.
For your convenience, we embed part of the Brave&#8217;s docs here.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>In the vast majority of cases you need to just use the <code class="literal">Tracer</code>
or <code class="literal">SpanCustomizer</code> beans from Brave that Sleuth provides. The documentation below contains
a high overview of what Brave is and how it works.</p></td></tr></table></div><p>Brave is a library used to capture and report latency information about distributed operations to Zipkin.
Most users do not use Brave directly. They use libraries or frameworks rather than employ Brave on their behalf.</p><p>This module includes a tracer that creates and joins spans that model the latency of potentially distributed work.
It also includes libraries to propagate the trace context over network boundaries (for example, with HTTP headers).</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_tracing" href="#_tracing"></a>51.1.1&nbsp;Tracing</h3></div></div></div><p>Most importantly, you need a <code class="literal">brave.Tracer</code>, configured to <a class="link" href="https://github.com/openzipkin/zipkin-reporter-java" target="_top">report to Zipkin</a>.</p><p>The following example setup sends trace data (spans) to Zipkin over HTTP (as opposed to Kafka):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyClass {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Tracer will be autowired</span>
MyClass(Tracer tracer) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer = tracer;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doSth() {
Span span = tracer.newTrace().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>).start();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
}
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If your span contains a name longer than 50 chars, then that name is truncated to 50 chars.
Your names have to be explicit and concrete.
Big names lead to latency issues and sometimes even thrown exceptions.</p></td></tr></table></div><p>The tracer creates and joins spans that model the latency of potentially distributed work.
It can employ sampling to reduce overhead during the process, to reduce the amount of data sent to Zipkin, or both.</p><p>Spans returned by a tracer report data to Zipkin when finished or do nothing if unsampled.
After starting a span, you can annotate events of interest or add tags containing details or lookup keys.</p><p>Spans have a context that includes trace identifiers that place the span at the correct spot in the tree representing the distributed operation.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_local_tracing" href="#_local_tracing"></a>51.1.2&nbsp;Local Tracing</h3></div></div></div><p>When tracing code that never leaves your process, run it inside a scoped span.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start a new trace or a span within an existing trace representing an operation</span>
ScopedSpan span = tracer.startScopedSpan(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// The span is in "scope" meaning downstream code such as loggers can see trace IDs</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> encoder.encode();
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (RuntimeException | Error e) {
span.error(e); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Unless you handle exceptions, you might not know the operation failed!</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> e;
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
span.finish(); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// always finish the span</span>
}</pre><p>When you need more features, or finer control, use the <code class="literal">Span</code> type:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start a new trace or a span within an existing trace representing an operation</span>
Span span = tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>).start();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Put the span in "scope" so that downstream code such as loggers can see trace IDs</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope ws = tracer.withSpanInScope(span)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> encoder.encode();
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (RuntimeException | Error e) {
span.error(e); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Unless you handle exceptions, you might not know the operation failed!</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> e;
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
span.finish(); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// note the scope is independent of the span. Always finish a span.</span>
}</pre><p>Both of the above examples report the exact same span on finish!</p><p>In the above example, the span will be either a new root span or the
next child in an existing trace.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_customizing_spans" href="#_customizing_spans"></a>51.1.3&nbsp;Customizing Spans</h3></div></div></div><p>Once you have a span, you can add tags to it.
The tags can be used as lookup keys or details.
For example, you might add a tag with your runtime version, as shown in the following example:</p><pre class="programlisting">span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"clnt/finagle.version"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"6.36.0"</span>);</pre><p>When exposing the ability to customize spans to third parties, prefer <code class="literal">brave.SpanCustomizer</code> as opposed to <code class="literal">brave.Span</code>.
The former is simpler to understand and test and does not tempt users with span lifecycle hooks.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> MyTraceCallback {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> request(Request request, SpanCustomizer customizer);
}</pre><p>Since <code class="literal">brave.Span</code> implements <code class="literal">brave.SpanCustomizer</code>, you can pass it to users, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> (MyTraceCallback callback : userCallbacks) {
callback.request(request, span);
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_implicitly_looking_up_the_current_span" href="#_implicitly_looking_up_the_current_span"></a>51.1.4&nbsp;Implicitly Looking up the Current Span</h3></div></div></div><p>Sometimes, you do not know if a trace is in progress or not, and you do not want users to do null checks.
<code class="literal">brave.CurrentSpanCustomizer</code> handles this problem by adding data to any span that&#8217;s in progress or drops, as shown in the following example:</p><p>Ex.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// The user code can then inject this without a chance of it being null.</span>
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> SpanCustomizer span;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> userCode() {
span.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"tx.started"</span>);
...
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_rpc_tracing" href="#_rpc_tracing"></a>51.1.5&nbsp;RPC tracing</h3></div></div></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Check for <a class="link" href="https://github.com/openzipkin/brave/tree/master/instrumentation" target="_top">instrumentation written here</a> and <a class="link" href="http://zipkin.io/pages/existing_instrumentations.html" target="_top">Zipkin&#8217;s list</a> before rolling your own RPC instrumentation.</p></td></tr></table></div><p>RPC tracing is often done automatically by interceptors. Behind the scenes, they add tags and events that relate to their role in an RPC operation.</p><p>The following example shows how to add a client span:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// before you send a request, add metadata that describes the operation</span>
span = tracer.nextSpan().name(service + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/"</span> + method).kind(CLIENT);
span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myrpc.version"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"1.0.0"</span>);
span.remoteServiceName(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"backend"</span>);
span.remoteIpAndPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"172.3.4.1"</span>, <span class="hl-number">8108</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Add the trace context to the request, so it can be propagated in-band</span>
tracing.propagation().injector(Request::addHeader)
.inject(span.context(), request);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when the request is scheduled, start the span</span>
span.start();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// if there is an error, tag the span</span>
span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"error"</span>, error.getCode());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// or if there is an exception</span>
span.error(exception);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when the response is complete, finish the span</span>
span.finish();</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_one_way_tracing" href="#_one_way_tracing"></a>One-Way tracing</h4></div></div></div><p>Sometimes, you need to model an asynchronous operation where there is a
request but no response. In normal RPC tracing, you use <code class="literal">span.finish()</code>
to indicate that the response was received. In one-way tracing, you use
<code class="literal">span.flush()</code> instead, as you do not expect a response.</p><p>The following example shows how a client might model a one-way operation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start a new span representing a client request</span>
oneWaySend = tracer.nextSpan().name(service + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/"</span> + method).kind(CLIENT);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Add the trace context to the request, so it can be propagated in-band</span>
tracing.propagation().injector(Request::addHeader)
.inject(oneWaySend.context(), request);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// fire off the request asynchronously, totally dropping any response</span>
request.execute();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start the client side and flush instead of finish</span>
oneWaySend.start().flush();</pre><p>The following example shows how a server might handle a one-way operation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// pull the context out of the incoming request</span>
extractor = tracing.propagation().extractor(Request::getHeader);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// convert that context to a span which you can name and add tags to</span>
oneWayReceive = nextSpan(tracer, extractor.extract(request))
.name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"process-request"</span>)
.kind(SERVER)
... add tags etc.
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start the server side and flush instead of finish</span>
oneWayReceive.start().flush();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// you should not modify this span anymore as it is complete. However,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// you can create children to represent follow-up work.</span>
next = tracer.newSpan(oneWayReceive.context()).name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"step2"</span>).start();</pre></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_sampling" href="#_sampling"></a>52.&nbsp;Sampling</h2></div></div></div><p>Sampling may be employed to reduce the data collected and reported out of process.
When a span is not sampled, it adds no overhead (a noop).</p><p>Sampling is an up-front decision, meaning that the decision to report data is made at the first operation in a trace and that decision is propagated downstream.</p><p>By default, a global sampler applies a single rate to all traced operations.
<code class="literal">Tracer.Builder.sampler</code> controls this setting, and it defaults to tracing every request.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_declarative_sampling" href="#_declarative_sampling"></a>52.1&nbsp;Declarative sampling</h2></div></div></div><p>Some applications need to sample based on the type or annotations of a java method.</p><p>Most users use a framework interceptor to automate this sort of policy.
The following example shows how that might work internally:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// derives a sample rate from an annotation on a java method</span>
DeclarativeSampler&lt;Traced&gt; sampler = DeclarativeSampler.create(Traced::sampleRate);
<em><span class="hl-annotation" style="color: gray">@Around("@annotation(traced)")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Object traceThing(ProceedingJoinPoint pjp, Traced traced) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Throwable {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// When there is no trace in progress, this decides using an annotation</span>
Sampler decideUsingAnnotation = declarativeSampler.toSampler(traced);
Tracer tracer = tracer.withSampler(decideUsingAnnotation);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// This code looks the same as if there was no declarative override</span>
ScopedSpan span = tracer.startScopedSpan(spanName(pjp));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> pjp.proceed();
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (RuntimeException | Error e) {
span.error(e);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> e;
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
span.finish();
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_custom_sampling" href="#_custom_sampling"></a>52.2&nbsp;Custom sampling</h2></div></div></div><p>Depending on what the operation is, you may want to apply different policies.
For example, you might not want to trace requests to static resources such as images, or you might want to trace all requests to a new api.</p><p>Most users use a framework interceptor to automate this sort of policy.
The following example shows how that might work internally:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Sampler fallback;
Span nextSpan(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Request input) {
Sampler requestBased = Sampler() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isSampled(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">long</span> traceId) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (input.url().startsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/experimental"</span>)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> true;
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (input.url().startsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/static"</span>)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> false;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> fallback.isSampled(traceId);
}
};
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> tracer.withSampler(requestBased).nextSpan();
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_sampling_in_spring_cloud_sleuth" href="#_sampling_in_spring_cloud_sleuth"></a>52.3&nbsp;Sampling in Spring Cloud Sleuth</h2></div></div></div><p>By default Spring Cloud Sleuth sets all spans to non-exportable.
That means that traces appear in logs but not in any remote store.
For testing the default is often enough, and it probably is all you need if you use only the logs (for example, with an ELK aggregator).
If you export span data to Zipkin, there is also an <code class="literal">Sampler.ALWAYS_SAMPLE</code> setting that exports everything and a <code class="literal">ProbabilityBasedSampler</code> setting that samples a fixed fraction of spans.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">ProbabilityBasedSampler</code> is the default if you use <code class="literal">spring-cloud-sleuth-zipkin</code>.
You can configure the exports by setting <code class="literal">spring.sleuth.sampler.probability</code>.
The passed value needs to be a double from <code class="literal">0.0</code> to <code class="literal">1.0</code>.</p></td></tr></table></div><p>A sampler can be installed by creating a bean definition, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Sampler defaultSampler() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Sampler.ALWAYS_SAMPLE;
}</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You can set the HTTP header <code class="literal">X-B3-Flags</code> to <code class="literal">1</code>, or, when doing messaging, you can set the <code class="literal">spanFlags</code> header to <code class="literal">1</code>.
Doing so forces the current span to be exportable regardless of the sampling decision.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_propagation" href="#_propagation"></a>53.&nbsp;Propagation</h2></div></div></div><p>Propagation is needed to ensure activities originating from the same root are collected together in the same trace.
The most common propagation approach is to copy a trace context from a client by sending an RPC request to a server receiving it.</p><p>For example, when a downstream HTTP call is made, its trace context is encoded as request headers and sent along with it, as shown in the following image:</p><pre class="screen"> Client Span Server Span
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474; &#9474; &#9474; &#9474;
&#9474; TraceContext &#9474; Http Request Headers &#9474; TraceContext &#9474;
&#9474; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9474; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9474; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9474;
&#9474; &#9474; TraceId &#9474; &#9474; &#9474; X&#9472;B3&#9472;TraceId &#9474; &#9474; &#9474; TraceId &#9474; &#9474;
&#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474;
&#9474; &#9474; ParentSpanId &#9474; &#9474; Extract &#9474; X&#9472;B3&#9472;ParentSpanId &#9474; Inject &#9474; &#9474; ParentSpanId &#9474; &#9474;
&#9474; &#9474; &#9500;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&gt;&#9474; &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&gt;&#9474; &#9474; &#9474;
&#9474; &#9474; SpanId &#9474; &#9474; &#9474; X&#9472;B3&#9472;SpanId &#9474; &#9474; &#9474; SpanId &#9474; &#9474;
&#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474;
&#9474; &#9474; Sampled &#9474; &#9474; &#9474; X&#9472;B3&#9472;Sampled &#9474; &#9474; &#9474; Sampled &#9474; &#9474;
&#9474; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474;
&#9474; &#9474; &#9474; &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</pre><p>The names above are from <a class="link" href="https://github.com/openzipkin/b3-propagation" target="_top">B3 Propagation</a>, which is built-in to Brave and has implementations in many languages and frameworks.</p><p>Most users use a framework interceptor to automate propagation.
The next two examples show how that might work for a client and a server.</p><p>The following example shows how client-side propagation might work:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// configure a function that injects a trace context into a request</span>
injector = tracing.propagation().injector(Request.Builder::addHeader);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// before a request is sent, add the current span's context to it</span>
injector.inject(span.context(), request);</pre><p>The following example shows how server-side propagation might work:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// configure a function that extracts the trace context from a request</span>
extractor = tracing.propagation().extractor(Request::getHeader);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when a server receives a request, it joins or starts a new trace</span>
span = tracer.nextSpan(extractor.extract(request));</pre><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_propagating_extra_fields" href="#_propagating_extra_fields"></a>53.1&nbsp;Propagating extra fields</h2></div></div></div><p>Sometimes you need to propagate extra fields, such as a request ID or an alternate trace context.
For example, if you are in a Cloud Foundry environment, you might want to pass the request ID, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when you initialize the builder, define the extra field you want to propagate</span>
Tracing.newBuilder().propagationFactory(
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-vcap-request-id"</span>)
);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// later, you can tag that request ID or use it in log correlation</span>
requestId = ExtraFieldPropagation.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-vcap-request-id"</span>);</pre><p>You may also need to propagate a trace context that you are not using.
For example, you may be in an Amazon Web Services environment but not be reporting data to X-Ray.
To ensure X-Ray can co-exist correctly, pass-through its tracing header, as shown in the following example:</p><pre class="programlisting">tracingBuilder.propagationFactory(
ExtraFieldPropagation.newFactory(B3Propagation.FACTORY, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-amzn-trace-id"</span>)
);</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>In Spring Cloud Sleuth all elements of the tracing builder <code class="literal">Tracing.newBuilder()</code>
are defined as beans. So if you want to pass a custom <code class="literal">PropagationFactory</code>, it&#8217;s enough
for you to create a bean of that type and we will set it in the <code class="literal">Tracing</code> bean.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="prefixed-fields" href="#prefixed-fields"></a>53.1.1&nbsp;Prefixed fields</h3></div></div></div><p>If they follow a common pattern, you can also prefix fields.
The following example shows how to propagate <code class="literal">x-vcap-request-id</code> the field as-is but send the <code class="literal">country-code</code> and <code class="literal">user-id</code> fields on the wire as <code class="literal">x-baggage-country-code</code> and <code class="literal">x-baggage-user-id</code>, respectively:</p><pre class="programlisting">Tracing.newBuilder().propagationFactory(
ExtraFieldPropagation.newFactoryBuilder(B3Propagation.FACTORY)
.addField(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-vcap-request-id"</span>)
.addPrefixedFields(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-baggage-"</span>, Arrays.asList(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"country-code"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"user-id"</span>))
.build()
);</pre><p>Later, you can call the following code to affect the country code of the current trace context:</p><pre class="programlisting">ExtraFieldPropagation.set(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-country-code"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FO"</span>);
String countryCode = ExtraFieldPropagation.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-country-code"</span>);</pre><p>Alternatively, if you have a reference to a trace context, you can use it explicitly, as shown in the following example:</p><pre class="programlisting">ExtraFieldPropagation.set(span.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-country-code"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FO"</span>);
String countryCode = ExtraFieldPropagation.get(span.context(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-country-code"</span>);</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>A difference from previous versions of Sleuth is that, with Brave, you must pass the list of baggage keys.
There are two properties to achieve this.
With the <code class="literal">spring.sleuth.baggage-keys</code>, you set keys that get prefixed with <code class="literal">baggage-</code> for HTTP calls and <code class="literal">baggage_</code> for messaging.
You can also use the <code class="literal">spring.sleuth.propagation-keys</code> property to pass a list of prefixed keys that are whitelisted without any prefix.
Notice that there&#8217;s no <code class="literal">x-</code> in front of the header keys.</p></td></tr></table></div><p>In order to automatically set the baggage values to Slf4j&#8217;s MDC, you have to set
the <code class="literal">spring.sleuth.log.slf4j.whitelisted-mdc-keys</code> property with a list of whitelisted
baggage and propagation keys. E.g. <code class="literal">spring.sleuth.log.slf4j.whitelisted-mdc-keys=foo</code> will set the value of the <code class="literal">foo</code> baggage into MDC.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Remember that adding entries to MDC can drastically decrease the performance of your application!</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_extracting_a_propagated_context" href="#_extracting_a_propagated_context"></a>53.1.2&nbsp;Extracting a Propagated Context</h3></div></div></div><p>The <code class="literal">TraceContext.Extractor&lt;C&gt;</code> reads trace identifiers and sampling status from an incoming request or message.
The carrier is usually a request object or headers.</p><p>This utility is used in standard instrumentation (such as <code class="literal">HttpServerHandler`</code>) but can also be used for custom RPC or messaging code.</p><p><code class="literal">TraceContextOrSamplingFlags</code> is usually used only with <code class="literal">Tracer.nextSpan(extracted)</code>, unless you are
sharing span IDs between a client and a server.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_sharing_span_ids_between_client_and_server" href="#_sharing_span_ids_between_client_and_server"></a>53.1.3&nbsp;Sharing span IDs between Client and Server</h3></div></div></div><p>A normal instrumentation pattern is to create a span representing the server side of an RPC.
<code class="literal">Extractor.extract</code> might return a complete trace context when applied to an incoming client request.
<code class="literal">Tracer.joinSpan</code> attempts to continue this trace, using the same span ID if supported or creating a child span
if not. When the span ID is shared, the reported data includes a flag saying so.</p><p>The following image shows an example of B3 propagation:</p><pre class="screen"> &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
Incoming Headers &#9474; TraceContext &#9474; &#9474; TraceContext &#9474;
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;(extract)&#9474; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9474;(join)&#9474; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9474;
&#9474; X&#9472;B3-TraceId &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; TraceId &#9474; &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; TraceId &#9474; &#9474;
&#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474;
&#9474; X&#9472;B3-ParentSpanId &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; ParentSpanId &#9474; &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; ParentSpanId &#9474; &#9474;
&#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474;
&#9474; X&#9472;B3-SpanId &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; SpanId &#9474; &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; SpanId &#9474; &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474;
&#9474; &#9474; &#9474; &#9474; &#9474; &#9474; Shared: true &#9474; &#9474;
&#9474; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474; &#9474; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</pre><p>Some propagation systems forward only the parent span ID, detected when <code class="literal">Propagation.Factory.supportsJoin() == false</code>.
In this case, a new span ID is always provisioned, and the incoming context determines the parent ID.</p><p>The following image shows an example of AWS propagation:</p><pre class="screen"> &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
x-amzn-trace-id &#9474; TraceContext &#9474; &#9474; TraceContext &#9474;
&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;(extract)&#9474; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9474;(join)&#9474; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9474;
&#9474; Root &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; TraceId &#9474; &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; TraceId &#9474; &#9474;
&#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474; &#9474;
&#9474; Parent &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; SpanId &#9474; &#9474;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9532;&gt; ParentSpanId &#9474; &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474; &#9474; &#9474; &#9474; &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474; &#9474; SpanId: New &#9474; &#9474;
&#9474; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</pre><p>Note: Some span reporters do not support sharing span IDs.
For example, if you set <code class="literal">Tracing.Builder.spanReporter(amazonXrayOrGoogleStackdrive)</code>, you should disable join by setting <code class="literal">Tracing.Builder.supportsJoin(false)</code>.
Doing so forces a new child span on <code class="literal">Tracer.joinSpan()</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_implementing_propagation" href="#_implementing_propagation"></a>53.1.4&nbsp;Implementing Propagation</h3></div></div></div><p><code class="literal">TraceContext.Extractor&lt;C&gt;</code> is implemented by a <code class="literal">Propagation.Factory</code> plugin.
Internally, this code creates the union type, <code class="literal">TraceContextOrSamplingFlags</code>, with one of the following:
* <code class="literal">TraceContext</code> if trace and span IDs were present.
* <code class="literal">TraceIdContext</code> if a trace ID was present but span IDs were not present.
* <code class="literal">SamplingFlags</code> if no identifiers were present.</p><p>Some <code class="literal">Propagation</code> implementations carry extra data from the point of extraction (for example, reading incoming headers) to injection (for example, writing outgoing headers).
For example, it might carry a request ID.
When implementations have extra data, they handle it as follows:
* If a <code class="literal">TraceContext</code> were extracted, add the extra data as <code class="literal">TraceContext.extra()</code>.
* Otherwise, add it as <code class="literal">TraceContextOrSamplingFlags.extra()</code>, which <code class="literal">Tracer.nextSpan</code> handles.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_current_tracing_component" href="#_current_tracing_component"></a>54.&nbsp;Current Tracing Component</h2></div></div></div><p>Brave supports a &#8220;current tracing component&#8221; concept, which should only be used when you have no other way to get a reference.
This was made for JDBC connections, as they often initialize prior to the tracing component.</p><p>The most recent tracing component instantiated is available through <code class="literal">Tracing.current()</code>.
You can also use <code class="literal">Tracing.currentTracer()</code> to get only the tracer.
If you use either of these methods, do not cache the result.
Instead, look them up each time you need them.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_current_span" href="#_current_span"></a>55.&nbsp;Current Span</h2></div></div></div><p>Brave supports a &#8220;current span&#8221; concept which represents the in-flight operation.
You can use <code class="literal">Tracer.currentSpan()</code> to add custom tags to a span and <code class="literal">Tracer.nextSpan()</code> to create a child of whatever is in-flight.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>In Sleuth, you can autowire the <code class="literal">Tracer</code> bean to retrieve the current span via
<code class="literal">tracer.currentSpan()</code> method. To retrieve the current context just call
<code class="literal">tracer.currentSpan().context()</code>. To get the current trace id as String
you can use the <code class="literal">traceIdString()</code> method like this: <code class="literal">tracer.currentSpan().context().traceIdString()</code>.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_setting_a_span_in_scope_manually" href="#_setting_a_span_in_scope_manually"></a>55.1&nbsp;Setting a span in scope manually</h2></div></div></div><p>When writing new instrumentation, it is important to place a span you created in scope as the current span.
Not only does doing so let users access it with <code class="literal">Tracer.currentSpan()</code>, but it also allows customizations such as SLF4J MDC to see the current trace IDs.</p><p><code class="literal">Tracer.withSpanInScope(Span)</code> facilitates this and is most conveniently employed by using the try-with-resources idiom.
Whenever external code might be invoked (such as proceeding an interceptor or otherwise), place the span in scope, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope ws = tracer.withSpanInScope(span)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> inboundRequest.invoke();
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// note the scope is independent of the span</span>
span.finish();
}</pre><p>In edge cases, you may need to clear the current span temporarily (for example, launching a task that should not be associated with the current request). To do tso, pass null to <code class="literal">withSpanInScope</code>, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope cleared = tracer.withSpanInScope(null)) {
startBackgroundThread();
}</pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_instrumentation" href="#_instrumentation"></a>56.&nbsp;Instrumentation</h2></div></div></div><p>Spring Cloud Sleuth automatically instruments all your Spring applications, so you should not have to do anything to activate it.
The instrumentation is added by using a variety of technologies according to the stack that is available. For example, for a servlet web application, we use a <code class="literal">Filter</code>, and, for Spring Integration, we use <code class="literal">ChannelInterceptors</code>.</p><p>You can customize the keys used in span tags.
To limit the volume of span data, an HTTP request is, by default, tagged only with a handful of metadata, such as the status code, the host, and the URL.
You can add request headers by configuring <code class="literal">spring.sleuth.keys.http.headers</code> (a list of header names).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Tags are collected and exported only if there is a <code class="literal">Sampler</code> that allows it. By default, there is no such <code class="literal">Sampler</code>, to ensure that there is no danger of accidentally collecting too much data without configuring something).</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_span_lifecycle" href="#_span_lifecycle"></a>57.&nbsp;Span lifecycle</h2></div></div></div><p>You can do the following operations on the Span by means of <code class="literal">brave.Tracer</code>:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="#creating-and-finishing-spans" title="57.1&nbsp;Creating and finishing spans">start</a>: When you start a span, its name is assigned and the start timestamp is recorded.</li><li class="listitem"><a class="link" href="#creating-and-finishing-spans" title="57.1&nbsp;Creating and finishing spans">close</a>: The span gets finished (the end time of the span is recorded) and, if the span is sampled, it is eligible for collection (for example, to Zipkin).</li><li class="listitem"><a class="link" href="#continuing-spans" title="57.2&nbsp;Continuing Spans">continue</a>: A new instance of span is created.
It is a copy of the one that it continues.</li><li class="listitem"><a class="link" href="#continuing-spans" title="57.2&nbsp;Continuing Spans">detach</a>: The span does not get stopped or closed.
It only gets removed from the current thread.</li><li class="listitem"><a class="link" href="#creating-spans-with-explicit-parent" title="57.3&nbsp;Creating a Span with an explicit Parent">create with explicit parent</a>: You can create a new span and set an explicit parent for it.</li></ul></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Sleuth creates an instance of <code class="literal">Tracer</code> for you. In order to use it, you can autowire it.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="creating-and-finishing-spans" href="#creating-and-finishing-spans"></a>57.1&nbsp;Creating and finishing spans</h2></div></div></div><p>You can manually create spans by using the <code class="literal">Tracer</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start a span. If there was a span present in this thread it will become</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the `newSpan`'s parent.</span>
Span newSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (Tracer.SpanInScope ws = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.withSpanInScope(newSpan.start())) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can tag a span</span>
newSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"taxValue"</span>, taxValue);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can log an event on a span</span>
newSpan.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"taxCalculated"</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Once done remember to finish the span. This will allow collecting</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the span to send it to Zipkin</span>
newSpan.finish();
}</pre><p>In the preceding example, we could see how to create a new instance of the span.
If there is already a span in this thread, it becomes the parent of the new span.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Always clean after you create a span. Also, always finish any span that you want to send to Zipkin.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If your span contains a name greater than 50 chars, that name is truncated to 50 chars.
Your names have to be explicit and concrete. Big names lead to latency issues and sometimes even exceptions.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="continuing-spans" href="#continuing-spans"></a>57.2&nbsp;Continuing Spans</h2></div></div></div><p>Sometimes, you do not want to create a new span but you want to continue one. An example of such a
situation might be as follows:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>AOP</strong></span>: If there was already a span created before an aspect was reached, you might not want to create a new span.</li><li class="listitem"><span class="strong"><strong>Hystrix</strong></span>: Executing a Hystrix command is most likely a logical part of the current processing.
It is in fact merely a technical implementation detail that you would not necessarily want to reflect in tracing as a separate being.</li></ul></div><p>To continue a span, you can use <code class="literal">brave.Tracer</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// let's assume that we're in a thread Y and we've received</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the `initialSpan` from thread X</span>
Span continuedSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.toSpan(newSpan.context());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can tag a span</span>
continuedSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"taxValue"</span>, taxValue);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can log an event on a span</span>
continuedSpan.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"taxCalculated"</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Once done remember to flush the span. That means that</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// it will get reported but the span itself is not yet finished</span>
continuedSpan.flush();
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="creating-spans-with-explicit-parent" href="#creating-spans-with-explicit-parent"></a>57.3&nbsp;Creating a Span with an explicit Parent</h2></div></div></div><p>You might want to start a new span and provide an explicit parent of that span.
Assume that the parent of a span is in one thread and you want to start a new span in another thread.
In Brave, whenever you call <code class="literal">nextSpan()</code>, it creates a span in reference to the span that is currently in scope.
You can put the span in scope and then call <code class="literal">nextSpan()</code>, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// let's assume that we're in a thread Y and we've received</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the `initialSpan` from thread X. `initialSpan` will be the parent</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// of the `newSpan`</span>
Span newSpan = null;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (Tracer.SpanInScope ws = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.withSpanInScope(initialSpan)) {
newSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateCommission"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can tag a span</span>
newSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"commissionValue"</span>, commissionValue);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// You can log an event on a span</span>
newSpan.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"commissionCalculated"</span>);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Once done remember to finish the span. This will allow collecting</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the span to send it to Zipkin. The tags and events set on the</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// newSpan will not be present on the parent</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (newSpan != null) {
newSpan.finish();
}
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>After creating such a span, you must finish it. Otherwise it is not reported (for example, to Zipkin).</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_naming_spans" href="#_naming_spans"></a>58.&nbsp;Naming spans</h2></div></div></div><p>Picking a span name is not a trivial task. A span name should depict an operation name.
The name should be low cardinality, so it should not include identifiers.</p><p>Since there is a lot of instrumentation going on, some span names are artificial:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">controller-method-name</code> when received by a Controller with a method name of <code class="literal">controllerMethodName</code></li><li class="listitem"><code class="literal">async</code> for asynchronous operations done with wrapped <code class="literal">Callable</code> and <code class="literal">Runnable</code> interfaces.</li><li class="listitem">Methods annotated with <code class="literal">@Scheduled</code> return the simple name of the class.</li></ul></div><p>Fortunately, for asynchronous processing, you can provide explicit naming.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="__literal_spanname_literal_annotation" href="#__literal_spanname_literal_annotation"></a>58.1&nbsp;<code class="literal">@SpanName</code> Annotation</h2></div></div></div><p>You can name the span explicitly by using the <code class="literal">@SpanName</code> annotation, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpanName("calculateTax")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> TaxCountingRunnable <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> Runnable {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> run() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// perform logic</span>
}
}</pre><p>In this case, when processed in the following manner, the span is named <code class="literal">calculateTax</code>:</p><pre class="programlisting">Runnable runnable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceRunnable(tracing, spanNamer,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TaxCountingRunnable());
Future&lt;?&gt; future = executorService.submit(runnable);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ... some additional logic ...</span>
future.get();</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="__literal_tostring_literal_method" href="#__literal_tostring_literal_method"></a>58.2&nbsp;<code class="literal">toString()</code> method</h2></div></div></div><p>It is pretty rare to create separate classes for <code class="literal">Runnable</code> or <code class="literal">Callable</code>.
Typically, one creates an anonymous instance of those classes.
You cannot annotate such classes.
To overcome that limitation, if there is no <code class="literal">@SpanName</code> annotation present, we check whether the class has a custom implementation of the <code class="literal">toString()</code> method.</p><p>Running such code leads to creating a span named <code class="literal">calculateTax</code>, as shown in the following example:</p><pre class="programlisting">Runnable runnable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceRunnable(tracing, spanNamer, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Runnable() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> run() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// perform logic</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String toString() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>;
}
});
Future&lt;?&gt; future = executorService.submit(runnable);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ... some additional logic ...</span>
future.get();</pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_managing_spans_with_annotations" href="#_managing_spans_with_annotations"></a>59.&nbsp;Managing Spans with Annotations</h2></div></div></div><p>You can manage spans with a variety of annotations.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_rationale" href="#_rationale"></a>59.1&nbsp;Rationale</h2></div></div></div><p>There are a number of good reasons to manage spans with annotations, including:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">API-agnostic means to collaborate with a span. Use of annotations lets users add to a span with no library dependency on a span api.
Doing so lets Sleuth change its core API to create less impact to user code.</li><li class="listitem">Reduced surface area for basic span operations. Without this feature, you must use the span api, which has lifecycle commands that could be used incorrectly.
By only exposing scope, tag, and log functionality, you can collaborate without accidentally breaking span lifecycle.</li><li class="listitem">Collaboration with runtime generated code. With libraries such as Spring Data and Feign, the implementations of interfaces are generated at runtime.
Consequently, span wrapping of objects was tedious.
Now you can provide annotations over interfaces and the arguments of those interfaces.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_creating_new_spans" href="#_creating_new_spans"></a>59.2&nbsp;Creating New Spans</h2></div></div></div><p>If you do not want to create local spans manually, you can use the <code class="literal">@NewSpan</code> annotation.
Also, we provide the <code class="literal">@SpanTag</code> annotation to add tags in an automated fashion.</p><p>Now we can consider some examples of usage.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod();</pre><p>Annotating the method without any parameter leads to creating a new span whose name equals the annotated method name.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan("customNameOnTestMethod4")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod4();</pre><p>If you provide the value in the annotation (either directly or by setting the <code class="literal">name</code> parameter), the created span has the provided value as the name.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// method declaration</span>
<em><span class="hl-annotation" style="color: gray">@NewSpan(name = "customNameOnTestMethod5")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod5(<em><span class="hl-annotation" style="color: gray">@SpanTag("testTag")</span></em> String param);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and method execution</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.testBean.testMethod5(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"test"</span>);</pre><p>You can combine both the name and a tag. Let&#8217;s focus on the latter.
In this case, the value of the annotated method&#8217;s parameter runtime value becomes the value of the tag.
In our sample, the tag key is <code class="literal">testTag</code>, and the tag value is <code class="literal">test</code>.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan(name = "customNameOnTestMethod3")</span></em>
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod3() {
}</pre><p>You can place the <code class="literal">@NewSpan</code> annotation on both the class and an interface.
If you override the interface&#8217;s method and provide a different value for the <code class="literal">@NewSpan</code> annotation, the most
concrete one wins (in this case <code class="literal">customNameOnTestMethod3</code> is set).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_continuing_spans" href="#_continuing_spans"></a>59.3&nbsp;Continuing Spans</h2></div></div></div><p>If you want to add tags and annotations to an existing span, you can use the <code class="literal">@ContinueSpan</code> annotation, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// method declaration</span>
<em><span class="hl-annotation" style="color: gray">@ContinueSpan(log = "testMethod11")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> testMethod11(<em><span class="hl-annotation" style="color: gray">@SpanTag("testTag11")</span></em> String param);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// method execution</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.testBean.testMethod11(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"test"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.testBean.testMethod13();</pre><p>(Note that, in contrast with the <code class="literal">@NewSpan</code> annotation ,you can also add logs with the <code class="literal">log</code> parameter.)</p><p>That way, the span gets continued and:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Log entries named <code class="literal">testMethod11.before</code> and <code class="literal">testMethod11.after</code> are created.</li><li class="listitem">If an exception is thrown, a log entry named <code class="literal">testMethod11.afterFailure</code> is also created.</li><li class="listitem">A tag with a key of <code class="literal">testTag11</code> and a value of <code class="literal">test</code> is created.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_advanced_tag_setting" href="#_advanced_tag_setting"></a>59.4&nbsp;Advanced Tag Setting</h2></div></div></div><p>There are 3 different ways to add tags to a span. All of them are controlled by the <code class="literal">SpanTag</code> annotation.
The precedence is as follows:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Try with a bean of <code class="literal">TagValueResolver</code> type and a provided name.</li><li class="listitem">If the bean name has not been provided, try to evaluate an expression.
We search for a <code class="literal">TagValueExpressionResolver</code> bean.
The default implementation uses SPEL expression resolution.
<span class="strong"><strong>IMPORTANT</strong></span> You can only reference properties from the SPEL expression. Method execution is not allowed due to security constraints.</li><li class="listitem">If we do not find any expression to evaluate, return the <code class="literal">toString()</code> value of the parameter.</li></ol></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_extractor" href="#_custom_extractor"></a>59.4.1&nbsp;Custom extractor</h3></div></div></div><p>The value of the tag for the following method is computed by an implementation of <code class="literal">TagValueResolver</code> interface.
Its class name has to be passed as the value of the <code class="literal">resolver</code> attribute.</p><p>Consider the following annotated method:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> getAnnotationForTagValueResolver(
<em><span class="hl-annotation" style="color: gray">@SpanTag(key = "test", resolver = TagValueResolver.class)</span></em> String test) {
}</pre><p>Now further consider the following <code class="literal">TagValueResolver</code> bean implementation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean(name = "myCustomTagValueResolver")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> TagValueResolver tagValueResolver() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> parameter -&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Value from myCustomTagValueResolver"</span>;
}</pre><p>The two preceding examples lead to setting a tag value equal to <code class="literal">Value from myCustomTagValueResolver</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_resolving_expressions_for_a_value" href="#_resolving_expressions_for_a_value"></a>59.4.2&nbsp;Resolving Expressions for a Value</h3></div></div></div><p>Consider the following annotated method:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> getAnnotationForTagValueExpression(
<em><span class="hl-annotation" style="color: gray">@SpanTag(key = "test", expression = "'hello' + ' characters'")</span></em> String test) {
}</pre><p>No custom implementation of a <code class="literal">TagValueExpressionResolver</code> leads to evaluation of the SPEL expression, and a tag with a value of <code class="literal">4 characters</code> is set on the span.
If you want to use some other expression resolution mechanism, you can create your own implementation of the bean.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_the_literal_tostring_literal_method" href="#_using_the_literal_tostring_literal_method"></a>59.4.3&nbsp;Using the <code class="literal">toString()</code> method</h3></div></div></div><p>Consider the following annotated method:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@NewSpan</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> getAnnotationForArgumentToString(<em><span class="hl-annotation" style="color: gray">@SpanTag("test")</span></em> Long param) {
}</pre><p>Running the preceding method with a value of <code class="literal">15</code> leads to setting a tag with a String value of <code class="literal">"15"</code>.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_customizations" href="#_customizations"></a>60.&nbsp;Customizations</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_http" href="#_http"></a>60.1&nbsp;HTTP</h2></div></div></div><p>If a customization of client / server parsing of the HTTP related spans is required,
just register a bean of type <code class="literal">brave.http.HttpClientParser</code> or
<code class="literal">brave.http.HttpServerParser</code>. If client /server sampling is required, just
register a bean of type <code class="literal">brave.http.HttpSampler</code> and name the bean
<code class="literal">sleuthClientSampler</code> for client sampler and <code class="literal">sleuthServerSampler</code> for server sampler.
For your convenience the <code class="literal">@ClientSampler</code> and <code class="literal">@ServerSampler</code>
annotations can be used to inject the proper beans or to
reference the bean names via their static String <code class="literal">NAME</code> fields.</p><p>Check out Brave&#8217;s code to see an example of how to make a path-based sampler
<a class="link" href="https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy" target="_top">https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy</a></p><p>If you want to completely rewrite the <code class="literal">HttpTracing</code> bean you can use the <code class="literal">SkipPatternProvider</code>
interface to retrieve the URL <code class="literal">Pattern</code> for spans that should be not sampled. Below you can see
an example of usage of <code class="literal">SkipPatternProvider</code> inside a server side, <code class="literal">HttpSampler</code>.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
<em><span class="hl-annotation" style="color: gray">@Bean(name = ServerSampler.NAME)</span></em>
HttpSampler myHttpSampler(SkipPatternProvider provider) {
Pattern pattern = provider.skipPattern();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpSampler() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> &lt;Req&gt; Boolean trySample(HttpAdapter&lt;Req, ?&gt; adapter, Req request) {
String url = adapter.path(request);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> shouldSkip = pattern.matcher(url).matches();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (shouldSkip) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> false;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
}
};
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="__literal_tracingfilter_literal" href="#__literal_tracingfilter_literal"></a>60.2&nbsp;<code class="literal">TracingFilter</code></h2></div></div></div><p>You can also modify the behavior of the <code class="literal">TracingFilter</code>, which is the component that is responsible for processing the input HTTP request and adding tags basing on the HTTP response.
You can customize the tags or modify the response headers by registering your own instance of the <code class="literal">TracingFilter</code> bean.</p><p>In the following example, we register the <code class="literal">TracingFilter</code> bean, add the <code class="literal">ZIPKIN-TRACE-ID</code> response header containing the current Span&#8217;s trace id, and add a tag with key <code class="literal">custom</code> and a value <code class="literal">tag</code> to the span.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Component</span></em>
<em><span class="hl-annotation" style="color: gray">@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyFilter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> GenericFilterBean {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Tracer tracer;
MyFilter(Tracer tracer) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer = tracer;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException, ServletException {
Span currentSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.currentSpan();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (currentSpan == null) {
chain.doFilter(request, response);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// for readability we're returning trace id in a hex form</span>
((HttpServletResponse) response).addHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"ZIPKIN-TRACE-ID"</span>,
currentSpan.context().traceIdString());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// we can also add some custom tags</span>
currentSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"custom"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"tag"</span>);
chain.doFilter(request, response);
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_custom_service_name" href="#_custom_service_name"></a>60.3&nbsp;Custom service name</h2></div></div></div><p>By default, Sleuth assumes that, when you send a span to Zipkin, you want the span&#8217;s service name to be equal to the value of the <code class="literal">spring.application.name</code> property.
That is not always the case, though.
There are situations in which you want to explicitly provide a different service name for all spans coming from your application.
To achieve that, you can pass the following property to your application to override that value (the example is for a service named <code class="literal">myService</code>):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.service.name</span>: myService</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customization_of_reported_spans" href="#_customization_of_reported_spans"></a>60.4&nbsp;Customization of Reported Spans</h2></div></div></div><p>Before reporting spans (for example, to Zipkin) you may want to modify that span in some way.
You can do so by using the <code class="literal">FinishedSpanHandler</code> interface.</p><p>In Sleuth, we generate spans with a fixed name.
Some users want to modify the name depending on values of tags.
You can implement the <code class="literal">FinishedSpanHandler</code> interface to alter that name.</p><p>The following example shows how to register two beans that implement <code class="literal">FinishedSpanHandler</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
FinishedSpanHandler handlerOne() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FinishedSpanHandler() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> handle(TraceContext traceContext, MutableSpan span) {
span.name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> true; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// keep this span</span>
}
};
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
FinishedSpanHandler handlerTwo() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FinishedSpanHandler() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> handle(TraceContext traceContext, MutableSpan span) {
span.name(span.name() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" bar"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> true; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// keep this span</span>
}
};
}</pre><p>The preceding example results in changing the name of the reported span to <code class="literal">foo bar</code>, just before it gets reported (for example, to Zipkin).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_host_locator" href="#_host_locator"></a>60.5&nbsp;Host Locator</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is about defining <span class="strong"><strong>host</strong></span> from service discovery.
It is <span class="strong"><strong>NOT</strong></span> about finding Zipkin through service discovery.</p></td></tr></table></div><p>To define the host that corresponds to a particular span, we need to resolve the host name and port.
The default approach is to take these values from server properties.
If those are not set, we try to retrieve the host name from the network interfaces.</p><p>If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the <code class="literal">spring.zipkin.locator.discovery.enabled</code> property (it is applicable for both HTTP-based and Stream-based span reporting), as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.locator.discovery.enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_sending_spans_to_zipkin" href="#_sending_spans_to_zipkin"></a>61.&nbsp;Sending Spans to Zipkin</h2></div></div></div><p>By default, if you add <code class="literal">spring-cloud-starter-zipkin</code> as a dependency to your project, when the span is closed, it is sent to Zipkin over HTTP.
The communication is asynchronous.
You can configure the URL by setting the <code class="literal">spring.zipkin.baseUrl</code> property, as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.baseUrl</span>: http://<span class="hl-number">192.168</span>.<span class="hl-number">99.100</span>:<span class="hl-number">9411</span>/</pre><p>If you want to find Zipkin through service discovery, you can pass the Zipkin&#8217;s service ID inside the URL, as shown in the following example for <code class="literal">zipkinserver</code> service ID:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.baseUrl</span>: http://zipkinserver/</pre><p>To disable this feature just set <code class="literal">spring.zipkin.discoveryClientEnabled</code> to `false.</p><p>When the Discovery Client feature is enabled, Sleuth uses
<code class="literal">LoadBalancerClient</code> to find the URL of the Zipkin Server. It means
that you can set up the load balancing configuration e.g. via Ribbon.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">zipkinserver</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ribbon</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ListOfServers</span>: host1,host2</pre><p>If you have web, rabbit, or kafka together on the classpath, you might need to pick the means by which you would like to send spans to zipkin.
To do so, set <code class="literal">web</code>, <code class="literal">rabbit</code>, or <code class="literal">kafka</code> to the <code class="literal">spring.zipkin.sender.type</code> property.
The following example shows setting the sender type for <code class="literal">web</code>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.sender.type</span>: web</pre><p>To customize the <code class="literal">RestTemplate</code> that sends spans to Zipkin via HTTP, you can register
the <code class="literal">ZipkinRestTemplateCustomizer</code> bean.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfig {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em> ZipkinRestTemplateCustomizer myCustomizer() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ZipkinRestTemplateCustomizer() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> customize(RestTemplate restTemplate) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// customize the RestTemplate</span>
}
};
}
}</pre><p>If, however, you would like to control the full process of creating the <code class="literal">RestTemplate</code>
object, you will have to create a bean of <code class="literal">zipkin2.reporter.Sender</code> type.</p><pre class="programlisting"> <em><span class="hl-annotation" style="color: gray">@Bean</span></em> Sender myRestTemplateSender(ZipkinProperties zipkin,
ZipkinRestTemplateCustomizer zipkinRestTemplateCustomizer) {
RestTemplate restTemplate = mySuperCustomRestTemplate();
zipkinRestTemplateCustomizer.customize(restTemplate);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> myCustomSender(zipkin, restTemplate);
}</pre></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_zipkin_stream_span_consumer" href="#_zipkin_stream_span_consumer"></a>62.&nbsp;Zipkin Stream Span Consumer</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>We recommend using Zipkin&#8217;s native support for message-based span sending.
Starting from the Edgware release, the Zipkin Stream server is deprecated.
In the Finchley release, it got removed.</p></td></tr></table></div><p>If for some reason you need to create the deprecated Stream Zipkin server, see the <a class="link" href="http://cloud.spring.io/spring-cloud-static/Dalston.SR4/multi/multi__span_data_as_messages.html#_zipkin_consumer" target="_top">Dalston Documentation</a>.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_integrations" href="#_integrations"></a>63.&nbsp;Integrations</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_opentracing" href="#_opentracing"></a>63.1&nbsp;OpenTracing</h2></div></div></div><p>Spring Cloud Sleuth is compatible with <a class="link" href="http://opentracing.io/" target="_top">OpenTracing</a>.
If you have OpenTracing on the classpath, we automatically register the OpenTracing <code class="literal">Tracer</code> bean.
If you wish to disable this, set <code class="literal">spring.sleuth.opentracing.enabled</code> to <code class="literal">false</code></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_runnable_and_callable" href="#_runnable_and_callable"></a>63.2&nbsp;Runnable and Callable</h2></div></div></div><p>If you wrap your logic in <code class="literal">Runnable</code> or <code class="literal">Callable</code>, you can wrap those classes in their Sleuth representative, as shown in the following example for <code class="literal">Runnable</code>:</p><pre class="programlisting">Runnable runnable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Runnable() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> run() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// do some work</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String toString() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spanNameFromToStringMethod"</span>;
}
};
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Manual `TraceRunnable` creation with explicit "calculateTax" Span name</span>
Runnable traceRunnable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceRunnable(tracing, spanNamer, runnable,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Wrapping `Runnable` with `Tracing`. That way the current span will be available</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// in the thread of `Runnable`</span>
Runnable traceRunnableFromTracer = tracing.currentTraceContext().wrap(runnable);</pre><p>The following example shows how to do so for <code class="literal">Callable</code>:</p><pre class="programlisting">Callable&lt;String&gt; callable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Callable&lt;String&gt;() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String call() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> someLogic();
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String toString() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spanNameFromToStringMethod"</span>;
}
};
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Manual `TraceCallable` creation with explicit "calculateTax" Span name</span>
Callable&lt;String&gt; traceCallable = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceCallable&lt;&gt;(tracing, spanNamer, callable,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Wrapping `Callable` with `Tracing`. That way the current span will be available</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// in the thread of `Callable`</span>
Callable&lt;String&gt; traceCallableFromTracer = tracing.currentTraceContext()
.wrap(callable);</pre><p>That way, you ensure that a new span is created and closed for each execution.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_hystrix" href="#_hystrix"></a>63.3&nbsp;Hystrix</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_concurrency_strategy" href="#_custom_concurrency_strategy"></a>63.3.1&nbsp;Custom Concurrency Strategy</h3></div></div></div><p>We register a custom <a class="link" href="https://github.com/Netflix/Hystrix/wiki/Plugins#concurrencystrategy" target="_top"><code class="literal">HystrixConcurrencyStrategy</code></a> called <code class="literal">TraceCallable</code> that wraps all <code class="literal">Callable</code> instances in their Sleuth representative.
The strategy either starts or continues a span, depending on whether tracing was already going on before the Hystrix command was called.
To disable the custom Hystrix Concurrency Strategy, set the <code class="literal">spring.sleuth.hystrix.strategy.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_manual_command_setting" href="#_manual_command_setting"></a>63.3.2&nbsp;Manual Command setting</h3></div></div></div><p>Assume that you have the following <code class="literal">HystrixCommand</code>:</p><pre class="programlisting">HystrixCommand&lt;String&gt; hystrixCommand = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HystrixCommand&lt;String&gt;(setter) {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> String run() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> someLogic();
}
};</pre><p>To pass the tracing information, you have to wrap the same logic in the Sleuth version of the <code class="literal">HystrixCommand</code>, which is called
<code class="literal">TraceCommand</code>, as shown in the following example:</p><pre class="programlisting">TraceCommand&lt;String&gt; traceCommand = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceCommand&lt;String&gt;(tracer, setter) {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String doRun() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> someLogic();
}
};</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_rxjava" href="#_rxjava"></a>63.4&nbsp;RxJava</h2></div></div></div><p>We registering a custom <a class="link" href="https://github.com/ReactiveX/RxJava/wiki/Plugins#rxjavaschedulershook" target="_top"><code class="literal">RxJavaSchedulersHook</code></a> that wraps all <code class="literal">Action0</code> instances in their Sleuth representative, which is called <code class="literal">TraceAction</code>.
The hook either starts or continues a span, depending on whether tracing was already going on before the Action was scheduled.
To disable the custom <code class="literal">RxJavaSchedulersHook</code>, set the <code class="literal">spring.sleuth.rxjava.schedulers.hook.enabled</code> to <code class="literal">false</code>.</p><p>You can define a list of regular expressions for thread names for which you do not want spans to be created.
To do so, provide a comma-separated list of regular expressions in the <code class="literal">spring.sleuth.rxjava.schedulers.ignoredthreads</code> property.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The suggest approach to reactive programming and Sleuth is to use
the Reactor support.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_http_integration" href="#_http_integration"></a>63.5&nbsp;HTTP integration</h2></div></div></div><p>Features from this section can be disabled by setting the <code class="literal">spring.sleuth.web.enabled</code> property with value equal to <code class="literal">false</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_http_filter" href="#_http_filter"></a>63.5.1&nbsp;HTTP Filter</h3></div></div></div><p>Through the <code class="literal">TracingFilter</code>, all sampled incoming requests result in creation of a Span.
That Span&#8217;s name is <code class="literal">http:</code> + the path to which the request was sent.
For example, if the request was sent to <code class="literal">/this/that</code> then the name will be <code class="literal">http:/this/that</code>.
You can configure which URIs you would like to skip by setting the <code class="literal">spring.sleuth.web.skipPattern</code> property.
If you have <code class="literal">ManagementServerProperties</code> on classpath, its value of <code class="literal">contextPath</code> gets appended to the provided skip pattern.
If you want to reuse the Sleuth&#8217;s default skip patterns and just append your own, pass those patterns by using the <code class="literal">spring.sleuth.web.additionalSkipPattern</code>.</p><p>To change the order of tracing filter registration, please set the
<code class="literal">spring.sleuth.web.filter-order</code> property.</p><p>To disable the filter that logs uncaught exceptions you can disable the
<code class="literal">spring.sleuth.web.exception-throwing-filter-enabled</code> property.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_handlerinterceptor" href="#_handlerinterceptor"></a>63.5.2&nbsp;HandlerInterceptor</h3></div></div></div><p>Since we want the span names to be precise, we use a <code class="literal">TraceHandlerInterceptor</code> that either wraps an existing <code class="literal">HandlerInterceptor</code> or is added directly to the list of existing <code class="literal">HandlerInterceptors</code>.
The <code class="literal">TraceHandlerInterceptor</code> adds a special request attribute to the given <code class="literal">HttpServletRequest</code>.
If the the <code class="literal">TracingFilter</code> does not see this attribute, it creates a &#8220;fallback&#8221; span, which is an additional span created on the server side so that the trace is presented properly in the UI.
If that happens, there is probably missing instrumentation.
In that case, please file an issue in Spring Cloud Sleuth.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_async_servlet_support" href="#_async_servlet_support"></a>63.5.3&nbsp;Async Servlet support</h3></div></div></div><p>If your controller returns a <code class="literal">Callable</code> or a <code class="literal">WebAsyncTask</code>, Spring Cloud Sleuth continues the existing span instead of creating a new one.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_webflux_support" href="#_webflux_support"></a>63.5.4&nbsp;WebFlux support</h3></div></div></div><p>Through <code class="literal">TraceWebFilter</code>, all sampled incoming requests result in creation of a Span.
That Span&#8217;s name is <code class="literal">http:</code> + the path to which the request was sent.
For example, if the request was sent to <code class="literal">/this/that</code>, the name is <code class="literal">http:/this/that</code>.
You can configure which URIs you would like to skip by using the <code class="literal">spring.sleuth.web.skipPattern</code> property.
If you have <code class="literal">ManagementServerProperties</code> on the classpath, its value of <code class="literal">contextPath</code> gets appended to the provided skip pattern.
If you want to reuse Sleuth&#8217;s default skip patterns and append your own, pass those patterns by using the <code class="literal">spring.sleuth.web.additionalSkipPattern</code>.</p><p>To change the order of tracing filter registration, please set the
<code class="literal">spring.sleuth.web.filter-order</code> property.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_dubbo_rpc_support" href="#_dubbo_rpc_support"></a>63.5.5&nbsp;Dubbo RPC support</h3></div></div></div><p>Via the integration with Brave, Spring Cloud Sleuth supports <a class="link" href="http://dubbo.io/" target="_top">Dubbo</a>.
It&#8217;s enough to add the <code class="literal">brave-instrumentation-dubbo-rpc</code> dependency:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>io.zipkin.brave<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>brave-instrumentation-dubbo-rpc<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>You need to also set a <code class="literal">dubbo.properties</code> file with the following contents:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">dubbo.provider.filter</span>=tracing
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">dubbo.consumer.filter</span>=tracing</pre><p>You can read more about Brave - Dubbo integration <a class="link" href="https://github.com/openzipkin/brave/tree/master/instrumentation/dubbo-rpc" target="_top">here</a>.
An example of Spring Cloud Sleuth and Dubbo can be found <a class="link" href="https://github.com/openzipkin/sleuth-webmvc-example/compare/add-dubbo-tracing" target="_top">here</a>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_http_client_integration" href="#_http_client_integration"></a>63.6&nbsp;HTTP Client Integration</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_synchronous_rest_template" href="#_synchronous_rest_template"></a>63.6.1&nbsp;Synchronous Rest Template</h3></div></div></div><p>We inject a <code class="literal">RestTemplate</code> interceptor to ensure that all the tracing information is passed to the requests.
Each time a call is made, a new Span is created.
It gets closed upon receiving the response.
To block the synchronous <code class="literal">RestTemplate</code> features, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You have to register <code class="literal">RestTemplate</code> as a bean so that the interceptors get injected.
If you create a <code class="literal">RestTemplate</code> instance with a <code class="literal">new</code> keyword, the instrumentation does NOT work.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_asynchronous_rest_template" href="#_asynchronous_rest_template"></a>63.6.2&nbsp;Asynchronous Rest Template</h3></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting with Sleuth <code class="literal">2.0.0</code>, we no longer register a bean of <code class="literal">AsyncRestTemplate</code> type.
It is up to you to create such a bean.
Then we instrument it.</p></td></tr></table></div><p>To block the <code class="literal">AsyncRestTemplate</code> features, set <code class="literal">spring.sleuth.web.async.client.enabled</code> to <code class="literal">false</code>.
To disable creation of the default <code class="literal">TraceAsyncClientHttpRequestFactoryWrapper</code>, set <code class="literal">spring.sleuth.web.async.client.factory.enabled</code>
to <code class="literal">false</code>.
If you do not want to create <code class="literal">AsyncRestClient</code> at all, set <code class="literal">spring.sleuth.web.async.client.template.enabled</code> to <code class="literal">false</code>.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_multiple_asynchronous_rest_templates" href="#_multiple_asynchronous_rest_templates"></a>Multiple Asynchronous Rest Templates</h4></div></div></div><p>Sometimes you need to use multiple implementations of the Asynchronous Rest Template.
In the following snippet, you can see an example of how to set up such a custom <code class="literal">AsyncRestTemplate</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
<em><span class="hl-annotation" style="color: gray">@Bean(name = "customAsyncRestTemplate")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> AsyncRestTemplate traceAsyncRestTemplate() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AsyncRestTemplate(asyncClientFactory(),
clientHttpRequestFactory());
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> ClientHttpRequestFactory clientHttpRequestFactory() {
ClientHttpRequestFactory clientHttpRequestFactory = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> CustomClientHttpRequestFactory();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// CUSTOMIZE HERE</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> clientHttpRequestFactory;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> AsyncClientHttpRequestFactory asyncClientFactory() {
AsyncClientHttpRequestFactory factory = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> CustomAsyncClientHttpRequestFactory();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// CUSTOMIZE HERE</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> factory;
}
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="__literal_webclient_literal" href="#__literal_webclient_literal"></a>63.6.3&nbsp;<code class="literal">WebClient</code></h3></div></div></div><p>We inject a <code class="literal">ExchangeFilterFunction</code> implementation that creates a span and, through on-success and on-error callbacks, takes care of closing client-side spans.</p><p>To block this feature, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You have to register <code class="literal">WebClient</code> as a bean so that the tracing instrumentation gets applied.
If you create a <code class="literal">WebClient</code> instance with a <code class="literal">new</code> keyword, the instrumentation does NOT work.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_traverson" href="#_traverson"></a>63.6.4&nbsp;Traverson</h3></div></div></div><p>If you use the <a class="link" href="http://docs.spring.io/spring-hateoas/docs/current/reference/html/#client.traverson" target="_top">Traverson</a> library, you can inject a <code class="literal">RestTemplate</code> as a bean into your Traverson object.
Since <code class="literal">RestTemplate</code> is already intercepted, you get full support for tracing in your client. The following pseudo code
shows how to do that:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> RestTemplate restTemplate;
Traverson traverson = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Traverson(URI.create(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://some/address"</span>),
MediaType.APPLICATION_JSON, MediaType.APPLICATION_JSON_UTF8).setRestOperations(restTemplate);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// use Traverson</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_apache_literal_httpclientbuilder_literal_and_literal_httpasyncclientbuilder_literal" href="#_apache_literal_httpclientbuilder_literal_and_literal_httpasyncclientbuilder_literal"></a>63.6.5&nbsp;Apache <code class="literal">HttpClientBuilder</code> and <code class="literal">HttpAsyncClientBuilder</code></h3></div></div></div><p>We instrument the <code class="literal">HttpClientBuilder</code> and <code class="literal">HttpAsyncClientBuilder</code> so that
tracing context gets injected to the sent requests.</p><p>To block these features, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_netty_literal_httpclient_literal" href="#_netty_literal_httpclient_literal"></a>63.6.6&nbsp;Netty <code class="literal">HttpClient</code></h3></div></div></div><p>We instrument the Netty&#8217;s <code class="literal">HttpClient</code>.</p><p>To block this feature, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You have to register <code class="literal">HttpClient</code> as a bean so that the instrumentation happens.
If you create a <code class="literal">HttpClient</code> instance with a <code class="literal">new</code> keyword, the instrumentation does NOT work.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="__literal_userinforesttemplatecustomizer_literal" href="#__literal_userinforesttemplatecustomizer_literal"></a>63.6.7&nbsp;<code class="literal">UserInfoRestTemplateCustomizer</code></h3></div></div></div><p>We instrument the Spring Security&#8217;s <code class="literal">UserInfoRestTemplateCustomizer</code>.</p><p>To block this feature, set <code class="literal">spring.sleuth.web.client.enabled</code> to <code class="literal">false</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_feign" href="#_feign"></a>63.7&nbsp;Feign</h2></div></div></div><p>By default, Spring Cloud Sleuth provides integration with Feign through <code class="literal">TraceFeignClientAutoConfiguration</code>.
You can disable it entirely by setting <code class="literal">spring.sleuth.feign.enabled</code> to <code class="literal">false</code>.
If you do so, no Feign-related instrumentation take place.</p><p>Part of Feign instrumentation is done through a <code class="literal">FeignBeanPostProcessor</code>.
You can disable it by setting <code class="literal">spring.sleuth.feign.processor.enabled</code> to <code class="literal">false</code>.
If you set it to <code class="literal">false</code>, Spring Cloud Sleuth does not instrument any of your custom Feign components.
However, all the default instrumentation is still there.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_asynchronous_communication" href="#_asynchronous_communication"></a>63.8&nbsp;Asynchronous Communication</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="__literal_async_literal_annotated_methods" href="#__literal_async_literal_annotated_methods"></a>63.8.1&nbsp;<code class="literal">@Async</code> Annotated methods</h3></div></div></div><p>In Spring Cloud Sleuth, we instrument async-related components so that the tracing information is passed between threads.
You can disable this behavior by setting the value of <code class="literal">spring.sleuth.async.enabled</code> to <code class="literal">false</code>.</p><p>If you annotate your method with <code class="literal">@Async</code>, we automatically create a new Span with the following characteristics:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">If the method is annotated with <code class="literal">@SpanName</code>, the value of the annotation is the Span&#8217;s name.</li><li class="listitem">If the method is not annotated with <code class="literal">@SpanName</code>, the Span name is the annotated method name.</li><li class="listitem">The span is tagged with the method&#8217;s class name and method name.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="__literal_scheduled_literal_annotated_methods" href="#__literal_scheduled_literal_annotated_methods"></a>63.8.2&nbsp;<code class="literal">@Scheduled</code> Annotated Methods</h3></div></div></div><p>In Spring Cloud Sleuth, we instrument scheduled method execution so that the tracing information is passed between threads.
You can disable this behavior by setting the value of <code class="literal">spring.sleuth.scheduled.enabled</code> to <code class="literal">false</code>.</p><p>If you annotate your method with <code class="literal">@Scheduled</code>, we automatically create a new span with the following characteristics:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The span name is the annotated method name.</li><li class="listitem">The span is tagged with the method&#8217;s class name and method name.</li></ul></div><p>If you want to skip span creation for some <code class="literal">@Scheduled</code> annotated classes, you can set the <code class="literal">spring.sleuth.scheduled.skipPattern</code> with a regular expression that matches the fully qualified name of the <code class="literal">@Scheduled</code> annotated class.
If you use <code class="literal">spring-cloud-sleuth-stream</code> and <code class="literal">spring-cloud-netflix-hystrix-stream</code> together, a span is created for each Hystrix metrics and sent to Zipkin.
This behavior may be annoying. That&#8217;s why, by default, <code class="literal">spring.sleuth.scheduled.skipPattern=org.springframework.cloud.netflix.hystrix.stream.HystrixStreamTask</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_executor_executorservice_and_scheduledexecutorservice" href="#_executor_executorservice_and_scheduledexecutorservice"></a>63.8.3&nbsp;Executor, ExecutorService, and ScheduledExecutorService</h3></div></div></div><p>We provide <code class="literal">LazyTraceExecutor</code>, <code class="literal">TraceableExecutorService</code>, and <code class="literal">TraceableScheduledExecutorService</code>. Those implementations create spans each time a new task is submitted, invoked, or scheduled.</p><p>The following example shows how to pass tracing information with <code class="literal">TraceableExecutorService</code> when working with <code class="literal">CompletableFuture</code>:</p><pre class="programlisting">CompletableFuture&lt;Long&gt; completableFuture = CompletableFuture.supplyAsync(() -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// perform some logic</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">1</span>_<span class="hl-number">000</span>_<span class="hl-number">000L</span>;
}, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TraceableExecutorService(beanFactory, executorService,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// 'calculateTax' explicitly names the span - this param is optional</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"calculateTax"</span>));</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Sleuth does not work with <code class="literal">parallelStream()</code> out of the box.
If you want to have the tracing information propagated through the stream, you have to use the approach with <code class="literal">supplyAsync(&#8230;&#8203;)</code>, as shown earlier.</p></td></tr></table></div><p>If there are beans that implement the <code class="literal">Executor</code> interface that you would like
to exclude from span creation, you can use the <code class="literal">spring.sleuth.async.ignored-beans</code>
property where you can provide a list of bean names.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_customization_of_executors" href="#_customization_of_executors"></a>Customization of Executors</h4></div></div></div><p>Sometimes, you need to set up a custom instance of the <code class="literal">AsyncExecutor</code>.
The following example shows how to set up such a custom <code class="literal">Executor</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAsync</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomExecutorConfig <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AsyncConfigurerSupport {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
BeanFactory beanFactory;
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ThreadPoolTaskExecutor();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// CUSTOMIZE HERE</span>
executor.setCorePoolSize(<span class="hl-number">7</span>);
executor.setMaxPoolSize(<span class="hl-number">42</span>);
executor.setQueueCapacity(<span class="hl-number">11</span>);
executor.setThreadNamePrefix(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"MyExecutor-"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// DON'T FORGET TO INITIALIZE</span>
executor.initialize();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LazyTraceExecutor(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.beanFactory, executor);
}
}</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_messaging" href="#_messaging"></a>63.9&nbsp;Messaging</h2></div></div></div><p>Features from this section can be disabled by setting the <code class="literal">spring.sleuth.messaging.enabled</code> property with value equal to <code class="literal">false</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_integration_and_spring_cloud_stream" href="#_spring_integration_and_spring_cloud_stream"></a>63.9.1&nbsp;Spring Integration and Spring Cloud Stream</h3></div></div></div><p>Spring Cloud Sleuth integrates with <a class="link" href="http://projects.spring.io/spring-integration/" target="_top">Spring Integration</a>.
It creates spans for publish and subscribe events.
To disable Spring Integration instrumentation, set <code class="literal">spring.sleuth.integration.enabled</code> to <code class="literal">false</code>.</p><p>You can provide the <code class="literal">spring.sleuth.integration.patterns</code> pattern to explicitly provide the names of channels that you want to include for tracing.
By default, all channels but <code class="literal">hystrixStreamOutput</code> channel are included.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>When using the <code class="literal">Executor</code> to build a Spring Integration <code class="literal">IntegrationFlow</code>, you must use the untraced version of the <code class="literal">Executor</code>.
Decorating the Spring Integration Executor Channel with <code class="literal">TraceableExecutorService</code> causes the spans to be improperly closed.</p></td></tr></table></div><p>If you want to customize the way tracing context is read from and written to message headers,
it&#8217;s enough for you to register beans of types:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">Propagation.Setter&lt;MessageHeaderAccessor, String&gt;</code> - for writing headers to the message</li><li class="listitem"><code class="literal">Propagation.Getter&lt;MessageHeaderAccessor, String&gt;</code> - for reading headers from the message</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_rabbitmq" href="#_spring_rabbitmq"></a>63.9.2&nbsp;Spring RabbitMq</h3></div></div></div><p>We instrument the <code class="literal">RabbitTemplate</code> so that tracing headers get injected
into the message.</p><p>To block this feature, set <code class="literal">spring.sleuth.messaging.rabbit.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_kafka" href="#_spring_kafka"></a>63.9.3&nbsp;Spring Kafka</h3></div></div></div><p>We instrument the Spring Kafka&#8217;s <code class="literal">ProducerFactory</code> and <code class="literal">ConsumerFactory</code>
so that tracing headers get injected into the created Spring Kafka&#8217;s
<code class="literal">Producer</code> and <code class="literal">Consumer</code>.</p><p>To block this feature, set <code class="literal">spring.sleuth.messaging.kafka.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_jms" href="#_spring_jms"></a>63.9.4&nbsp;Spring JMS</h3></div></div></div><p>We instrument the <code class="literal">JmsTemplate</code> so that tracing headers get injected
into the message. We also support <code class="literal">@JmsListener</code> annotated methods on the consumer side.</p><p>To block this feature, set <code class="literal">spring.sleuth.messaging.jms.enabled</code> to <code class="literal">false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>We don&#8217;t support baggage propagation for JMS</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_zuul_2" href="#_zuul_2"></a>63.10&nbsp;Zuul</h2></div></div></div><p>We instrument the Zuul Ribbon integration by enriching the Ribbon requests with tracing information.
To disable Zuul support, set the <code class="literal">spring.sleuth.zuul.enabled</code> property to <code class="literal">false</code>.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_running_examples" href="#_running_examples"></a>64.&nbsp;Running examples</h2></div></div></div><p>You can see the running examples deployed in the <a class="link" href="https://run.pivotal.io/" target="_top">Pivotal Web Services</a>.
Check them out at the following links:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="http://docssleuth-zipkin-server.cfapps.io/" target="_top">Zipkin for apps presented in the samples to the top</a>. First make
a request to <a class="link" href="http://docssleuth-service1.cfapps.io/start" target="_top">Service 1</a> and then check out the trace in Zipkin.</li><li class="listitem"><a class="link" href="http://docsbrewing-zipkin-server.cfapps.io/" target="_top">Zipkin for Brewery on PWS</a>, its <a class="link" href="https://github.com/spring-cloud-samples/brewery" target="_top">Github Code</a>.
Ensure that you&#8217;ve picked the lookback period of 7 days. If there are no traces, go to <a class="link" href="https://docsbrewing-presenting.cfapps.io/" target="_top">Presenting application</a>
and order some beers. Then check Zipkin for traces.</li></ul></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_consul" href="#_spring_cloud_consul"></a>Part&nbsp;IX.&nbsp;Spring Cloud Consul</h1></div></div></div><div class="partintro"><div></div><p><span class="strong"><strong>Greenwich.M3</strong></span></p><p>This project provides Consul integrations for Spring Boot apps through autoconfiguration
and binding to the Spring Environment and other Spring programming model idioms. With a few
simple annotations you can quickly enable and configure the common patterns inside your
application and build large distributed systems with Consul based components. The
patterns provided include Service Discovery, Control Bus and Configuration.
Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon), Circuit Breaker
(Hystrix) are provided by integration with Spring Cloud Netflix.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-consul-install" href="#spring-cloud-consul-install"></a>65.&nbsp;Install Consul</h2></div></div></div><p>Please see the <a class="link" href="https://www.consul.io/intro/getting-started/install.html" target="_top">installation documentation</a> for instructions on how to install Consul.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-consul-agent" href="#spring-cloud-consul-agent"></a>66.&nbsp;Consul Agent</h2></div></div></div><p>A Consul Agent client must be available to all Spring Cloud Consul applications. By default, the Agent client is expected to be at <code class="literal">localhost:8500</code>. See the <a class="link" href="https://consul.io/docs/agent/basics.html" target="_top">Agent documentation</a> for specifics on how to start an Agent client and how to connect to a cluster of Consul Agent Servers. For development, after you have installed consul, you may start a Consul Agent using the following command:</p><pre class="screen">./src/main/bash/local_run_consul.sh</pre><p>This will start an agent in server mode on port 8500, with the ui available at <a class="link" href="http://localhost:8500" target="_top">http://localhost:8500</a></p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-consul-discovery" href="#spring-cloud-consul-discovery"></a>67.&nbsp;Service Discovery with Consul</h2></div></div></div><p>Service Discovery is one of the key tenets of a microservice based architecture. Trying to hand configure each client or some form of convention can be very difficult to do and can be very brittle. Consul provides Service Discovery services via an <a class="link" href="https://www.consul.io/docs/agent/http.html" target="_top">HTTP API</a> and <a class="link" href="https://www.consul.io/docs/agent/dns.html" target="_top">DNS</a>. Spring Cloud Consul leverages the HTTP API for service registration and discovery. This does not prevent non-Spring Cloud applications from leveraging the DNS interface. Consul Agents servers are run in a <a class="link" href="https://www.consul.io/docs/internals/architecture.html" target="_top">cluster</a> that communicates via a <a class="link" href="https://www.consul.io/docs/internals/gossip.html" target="_top">gossip protocol</a> and uses the <a class="link" href="https://www.consul.io/docs/internals/consensus.html" target="_top">Raft consensus protocol</a>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_to_activate" href="#_how_to_activate"></a>67.1&nbsp;How to activate</h2></div></div></div><p>To activate Consul Service Discovery use the starter with group <code class="literal">org.springframework.cloud</code> and artifact id <code class="literal">spring-cloud-starter-consul-discovery</code>. See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_with_consul" href="#_registering_with_consul"></a>67.2&nbsp;Registering with Consul</h2></div></div></div><p>When a client registers with Consul, it provides meta-data about itself such as host and port, id, name and tags. An HTTP <a class="link" href="https://www.consul.io/docs/agent/checks.html" target="_top">Check</a> is created by default that Consul hits the <code class="literal">/health</code> endpoint every 10 seconds. If the health check fails, the service instance is marked as critical.</p><p>Example Consul client:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@RequestMapping("/")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello world"</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).web(true).run(args);
}
}</pre><p>(i.e. utterly normal Spring Boot app). If the Consul client is located somewhere other than <code class="literal">localhost:8500</code>, the configuration is required to locate the client. Example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
host: localhost
port: 8500</pre><p>
</p><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p>If you use <a class="link" href="#spring-cloud-consul-config" title="68.&nbsp;Distributed Configuration with Consul">Spring Cloud Consul Config</a>, the above values will need to be placed in <code class="literal">bootstrap.yml</code> instead of <code class="literal">application.yml</code>.</p></td></tr></table></div><p>The default service name, instance id and port, taken from the <code class="literal">Environment</code>, are <code class="literal">${spring.application.name}</code>, the Spring Context ID and <code class="literal">${server.port}</code> respectively.</p><p>To disable the Consul Discovery Client you can set <code class="literal">spring.cloud.consul.discovery.enabled</code> to <code class="literal">false</code>.</p><p>To disable the service registration you can set <code class="literal">spring.cloud.consul.discovery.register</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_http_health_check" href="#_http_health_check"></a>67.3&nbsp;HTTP Health Check</h2></div></div></div><p>The health check for a Consul instance defaults to "/health", which is the default locations of a useful endpoint in a Spring Boot Actuator application. You need to change these, even for an Actuator application if you use a non-default context path or servlet path (e.g. <code class="literal">server.servletPath=/foo</code>) or management endpoint path (e.g. <code class="literal">management.server.servlet.context-path=/admin</code>). The interval that Consul uses to check the health endpoint may also be configured. "10s" and "1m" represent 10 seconds and 1 minute respectively. Example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
discovery:
healthCheckPath: ${management.server.servlet.context-path}/health
healthCheckInterval: 15s</pre><p>
</p><p>You can disable the health check by setting <code class="literal">management.health.consul.enabled=false</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_metadata_and_consul_tags" href="#_metadata_and_consul_tags"></a>67.3.1&nbsp;Metadata and Consul tags</h3></div></div></div><p>Consul does not yet support metadata on services. Spring Cloud&#8217;s <code class="literal">ServiceInstance</code> has a <code class="literal">Map&lt;String, String&gt; metadata</code> field. Spring Cloud Consul uses Consul tags to approximate metadata until Consul officially supports metadata. Tags with the form <code class="literal">key=value</code> will be split and used as a <code class="literal">Map</code> key and value respectively. Tags without the equal <code class="literal">=</code> sign, will be used as both the key and value.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
discovery:
tags: foo=bar, baz</pre><p>
</p><p>The above configuration will result in a map with <code class="literal">foo&#8594;bar</code> and <code class="literal">baz&#8594;baz</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_making_the_consul_instance_id_unique" href="#_making_the_consul_instance_id_unique"></a>67.3.2&nbsp;Making the Consul Instance ID Unique</h3></div></div></div><p>By default a consul instance is registered with an ID that is equal to its Spring Application Context ID. By default, the Spring Application Context ID is <code class="literal">${spring.application.name}:comma,separated,profiles:${server.port}</code>. For most cases, this will allow multiple instances of one service to run on one machine. If further uniqueness is required, Using Spring Cloud you can override this by providing a unique identifier in <code class="literal">spring.cloud.consul.discovery.instanceId</code>. For example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
discovery:
instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}</pre><p>
</p><p>With this metadata, and multiple service instances deployed on localhost, the random value will kick in there to make the instance unique. In Cloudfoundry the <code class="literal">vcap.application.instance_id</code> will be populated automatically in a Spring Boot application, so the random value will not be needed.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_applying_headers_to_health_check_requests" href="#_applying_headers_to_health_check_requests"></a>67.3.3&nbsp;Applying Headers to Health Check Requests</h3></div></div></div><p>Headers can be applied to health check requests. For example, if you&#8217;re trying to register a <a class="link" href="https://cloud.spring.io/spring-cloud-config/" target="_top">Spring Cloud Config</a> server that uses <a class="link" href="https://github.com/spring-cloud/spring-cloud-config/blob/master/docs/src/main/asciidoc/spring-cloud-config.adoc#vault-backend" target="_top">Vault Backend</a>:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
discovery:
health-check-headers:
X-Config-Token: 6442e58b-d1ea-182e-cfa5-cf9cddef0722</pre><p>
</p><p>According to the HTTP standard, each header can have more than one values, in which case, an array can be supplied:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
discovery:
health-check-headers:
X-Config-Token:
- "6442e58b-d1ea-182e-cfa5-cf9cddef0722"
- "Some other value"</pre><p>
</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_looking_up_services" href="#_looking_up_services"></a>67.4&nbsp;Looking up services</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_ribbon" href="#_using_ribbon"></a>67.4.1&nbsp;Using Ribbon</h3></div></div></div><p>Spring Cloud has support for <a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign" target="_top">Feign</a> (a REST client builder) and also <a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-ribbon" target="_top">Spring <code class="literal">RestTemplate</code></a>
for looking up services using the logical service names/ids instead of physical URLs. Both Feign and the discovery-aware RestTemplate utilize <a class="link" href="http://cloud.spring.io/spring-cloud-netflix/single/spring-cloud-netflix.html#spring-cloud-ribbon" target="_top">Ribbon</a> for client-side load balancing.</p><p>If you want to access service STORES using the RestTemplate simply declare:</p><pre class="screen">@LoadBalanced
@Bean
public RestTemplate loadbalancedRestTemplate() {
new RestTemplate();
}</pre><p>and use it like this (notice how we use the STORES service name/id from Consul instead of a fully qualified domainname):</p><pre class="screen">@Autowired
RestTemplate restTemplate;
public String getFirstProduct() {
return this.restTemplate.getForObject("https://STORES/products/1", String.class);
}</pre><p>If you have Consul clusters in multiple datacenters and you want to access a service in another datacenter a service name/id alone is not enough. In that case
you use property <code class="literal">spring.cloud.consul.discovery.datacenters.STORES=dc-west</code> where <code class="literal">STORES</code> is the service name/id and <code class="literal">dc-west</code> is the datacenter
where the STORES service lives.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_using_the_discoveryclient" href="#_using_the_discoveryclient"></a>67.4.2&nbsp;Using the DiscoveryClient</h3></div></div></div><p>You can also use the <code class="literal">org.springframework.cloud.client.discovery.DiscoveryClient</code> which provides a simple API for discovery clients that is not specific to Netflix, e.g.</p><pre class="screen">@Autowired
private DiscoveryClient discoveryClient;
public String serviceUrl() {
List&lt;ServiceInstance&gt; list = discoveryClient.getInstances("STORES");
if (list != null &amp;&amp; list.size() &gt; 0 ) {
return list.get(0).getUri();
}
return null;
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_consul_catalog_watch" href="#_consul_catalog_watch"></a>67.5&nbsp;Consul Catalog Watch</h2></div></div></div><p>The Consul Catalog Watch takes advantage of the ability of consul to <a class="link" href="https://www.consul.io/docs/agent/watches.html#services" target="_top">watch services</a>. The Catalog Watch makes a blocking Consul HTTP API call to determine if any services have changed. If there is new service data a Heartbeat Event is published.</p><p>To change the frequency of when the Config Watch is called change <code class="literal">spring.cloud.consul.config.discovery.catalog-services-watch-delay</code>. The default value is 1000, which is in milliseconds. The delay is the amount of time after the end of the previous invocation and the start of the next.</p><p>To disable the Catalog Watch set <code class="literal">spring.cloud.consul.discovery.catalogServicesWatch.enabled=false</code>.</p><p>The watch uses a Spring <code class="literal">TaskScheduler</code> to schedule the call to consul. By default it is a <code class="literal">ThreadPoolTaskScheduler</code> with a <code class="literal">poolSize</code> of 1. To change the <code class="literal">TaskScheduler</code>, create a bean of type <code class="literal">TaskScheduler</code> named with the <code class="literal">ConsulDiscoveryClientConfiguration.CATALOG_WATCH_TASK_SCHEDULER_NAME</code> constant.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-consul-config" href="#spring-cloud-consul-config"></a>68.&nbsp;Distributed Configuration with Consul</h2></div></div></div><p>Consul provides a <a class="link" href="https://consul.io/docs/agent/http/kv.html" target="_top">Key/Value Store</a> for storing configuration and other metadata. Spring Cloud Consul Config is an alternative to the <a class="link" href="https://github.com/spring-cloud/spring-cloud-config" target="_top">Config Server and Client</a>. Configuration is loaded into the Spring Environment during the special "bootstrap" phase. Configuration is stored in the <code class="literal">/config</code> folder by default. Multiple <code class="literal">PropertySource</code> instances are created based on the application&#8217;s name and the active profiles that mimicks the Spring Cloud Config order of resolving properties. For example, an application with the name "testApp" and with the "dev" profile will have the following property sources created:</p><pre class="screen">config/testApp,dev/
config/testApp/
config/application,dev/
config/application/</pre><p>The most specific property source is at the top, with the least specific at the bottom. Properties in the <code class="literal">config/application</code> folder are applicable to all applications using consul for configuration. Properties in the <code class="literal">config/testApp</code> folder are only available to the instances of the service named "testApp".</p><p>Configuration is currently read on startup of the application. Sending a HTTP POST to <code class="literal">/refresh</code> will cause the configuration to be reloaded. <a class="xref" href="#spring-cloud-consul-config-watch" title="68.3&nbsp;Config Watch">Section&nbsp;68.3, &#8220;Config Watch&#8221;</a> will also automatically detect changes and reload the application context.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_to_activate_2" href="#_how_to_activate_2"></a>68.1&nbsp;How to activate</h2></div></div></div><p>To get started with Consul Configuration use the starter with group <code class="literal">org.springframework.cloud</code> and artifact id <code class="literal">spring-cloud-starter-consul-config</code>. See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p><p>This will enable auto-configuration that will setup Spring Cloud Consul Config.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customizing" href="#_customizing"></a>68.2&nbsp;Customizing</h2></div></div></div><p>Consul Config may be customized using the following properties:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
config:
enabled: true
prefix: configuration
defaultContext: apps
profileSeparator: '::'</pre><p>
</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to "false" disables Consul Config</li><li class="listitem"><code class="literal">prefix</code> sets the base folder for configuration values</li><li class="listitem"><code class="literal">defaultContext</code> sets the folder name used by all applications</li><li class="listitem"><code class="literal">profileSeparator</code> sets the value of the separator used to separate the profile name in property sources with profiles</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-consul-config-watch" href="#spring-cloud-consul-config-watch"></a>68.3&nbsp;Config Watch</h2></div></div></div><p>The Consul Config Watch takes advantage of the ability of consul to <a class="link" href="https://www.consul.io/docs/agent/watches.html#keyprefix" target="_top">watch a key prefix</a>. The Config Watch makes a blocking Consul HTTP API call to determine if any relevant configuration data has changed for the current application. If there is new configuration data a Refresh Event is published. This is equivalent to calling the <code class="literal">/refresh</code> actuator endpoint.</p><p>To change the frequency of when the Config Watch is called change <code class="literal">spring.cloud.consul.config.watch.delay</code>. The default value is 1000, which is in milliseconds. The delay is the amount of time after the end of the previous invocation and the start of the next.</p><p>To disable the Config Watch set <code class="literal">spring.cloud.consul.config.watch.enabled=false</code>.</p><p>The watch uses a Spring <code class="literal">TaskScheduler</code> to schedule the call to consul. By default it is a <code class="literal">ThreadPoolTaskScheduler</code> with a <code class="literal">poolSize</code> of 1. To change the <code class="literal">TaskScheduler</code>, create a bean of type <code class="literal">TaskScheduler</code> named with the <code class="literal">ConsulConfigAutoConfiguration.CONFIG_WATCH_TASK_SCHEDULER_NAME</code> constant.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-consul-config-format" href="#spring-cloud-consul-config-format"></a>68.4&nbsp;YAML or Properties with Config</h2></div></div></div><p>It may be more convenient to store a blob of properties in YAML or Properties format as opposed to individual key/value pairs. Set the <code class="literal">spring.cloud.consul.config.format</code> property to <code class="literal">YAML</code> or <code class="literal">PROPERTIES</code>. For example to use YAML:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
config:
format: YAML</pre><p>
</p><p>YAML must be set in the appropriate <code class="literal">data</code> key in consul. Using the defaults above the keys would look like:</p><pre class="screen">config/testApp,dev/data
config/testApp/data
config/application,dev/data
config/application/data</pre><p>You could store a YAML document in any of the keys listed above.</p><p>You can change the data key using <code class="literal">spring.cloud.consul.config.data-key</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-consul-config-git2consul" href="#spring-cloud-consul-config-git2consul"></a>68.5&nbsp;git2consul with Config</h2></div></div></div><p>git2consul is a Consul community project that loads files from a git repository to individual keys into Consul. By default the names of the keys are names of the files. YAML and Properties files are supported with file extensions of <code class="literal">.yml</code> and <code class="literal">.properties</code> respectively. Set the <code class="literal">spring.cloud.consul.config.format</code> property to <code class="literal">FILES</code>. For example:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="screen">spring:
cloud:
consul:
config:
format: FILES</pre><p>
</p><p>Given the following keys in <code class="literal">/config</code>, the <code class="literal">development</code> profile and an application name of <code class="literal">foo</code>:</p><pre class="screen">.gitignore
application.yml
bar.properties
foo-development.properties
foo-production.yml
foo.properties
master.ref</pre><p>the following property sources would be created:</p><pre class="screen">config/foo-development.properties
config/foo.properties
config/application.yml</pre><p>The value of each key needs to be a properly formatted YAML or Properties file.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-consul-failfast" href="#spring-cloud-consul-failfast"></a>68.6&nbsp;Fail Fast</h2></div></div></div><p>It may be convenient in certain circumstances (like local development or certain test scenarios) to not fail if consul isn&#8217;t available for configuration. Setting <code class="literal">spring.cloud.consul.config.failFast=false</code> in <code class="literal">bootstrap.yml</code> will cause the configuration module to log a warning rather than throw an exception. This will allow the application to continue startup normally.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-consul-retry" href="#spring-cloud-consul-retry"></a>69.&nbsp;Consul Retry</h2></div></div></div><p>If you expect that the consul agent may occasionally be unavailable when
your app starts, you can ask it to keep trying after a failure. You need to add
<code class="literal">spring-retry</code> and <code class="literal">spring-boot-starter-aop</code> to your classpath. The default
behaviour is to retry 6 times with an initial backoff interval of 1000ms and an
exponential multiplier of 1.1 for subsequent backoffs. You can configure these
properties (and others) using <code class="literal">spring.cloud.consul.retry.*</code> configuration properties.
This works with both Spring Cloud Consul Config and Discovery registration.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>To take full control of the retry add a <code class="literal">@Bean</code> of type
<code class="literal">RetryOperationsInterceptor</code> with id "consulRetryInterceptor". Spring
Retry has a <code class="literal">RetryInterceptorBuilder</code> that makes it easy to create one.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-consul-bus" href="#spring-cloud-consul-bus"></a>70.&nbsp;Spring Cloud Bus with Consul</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_to_activate_3" href="#_how_to_activate_3"></a>70.1&nbsp;How to activate</h2></div></div></div><p>To get started with the Consul Bus use the starter with group <code class="literal">org.springframework.cloud</code> and artifact id <code class="literal">spring-cloud-starter-consul-bus</code>. See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p><p>See the <a class="link" href="https://cloud.spring.io/spring-cloud-bus/" target="_top">Spring Cloud Bus</a> documentation for the available actuator endpoints and howto send custom messages.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-consul-hystrix" href="#spring-cloud-consul-hystrix"></a>71.&nbsp;Circuit Breaker with Hystrix</h2></div></div></div><p>Applications can use the Hystrix Circuit Breaker provided by the Spring Cloud Netflix project by including this starter in the projects pom.xml: <code class="literal">spring-cloud-starter-hystrix</code>. Hystrix doesn&#8217;t depend on the Netflix Discovery Client. The <code class="literal">@EnableHystrix</code> annotation should be placed on a configuration class (usually the main class). Then methods can be annotated with <code class="literal">@HystrixCommand</code> to be protected by a circuit breaker. See <a class="link" href="http://projects.spring.io/spring-cloud/spring-cloud.html#_circuit_breaker_hystrix_clients" target="_top">the documentation</a> for more details.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-consul-turbine" href="#spring-cloud-consul-turbine"></a>72.&nbsp;Hystrix metrics aggregation with Turbine and Consul</h2></div></div></div><p>Turbine (provided by the Spring Cloud Netflix project), aggregates multiple instances Hystrix metrics streams, so the dashboard can display an aggregate view. Turbine uses the <code class="literal">DiscoveryClient</code> interface to lookup relevant instances. To use Turbine with Spring Cloud Consul, configure the Turbine application in a manner similar to the following examples:</p><p><b>pom.xml.&nbsp;</b>
</p><pre class="screen">&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-netflix-turbine&lt;/artifactId&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-starter-consul-discovery&lt;/artifactId&gt;
&lt;/dependency&gt;</pre><p>
</p><p>Notice that the Turbine dependency is not a starter. The turbine starter includes support for Netflix Eureka.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="screen">spring.application.name: turbine
applications: consulhystrixclient
turbine:
aggregator:
clusterConfig: ${applications}
appConfig: ${applications}</pre><p>
</p><p>The <code class="literal">clusterConfig</code> and <code class="literal">appConfig</code> sections must match, so it&#8217;s useful to put the comma-separated list of service ID&#8217;s into a separate configuration property.</p><p><b>Turbine.java.&nbsp;</b>
</p><pre class="screen">@EnableTurbine
@SpringBootApplication
public class Turbine {
public static void main(String[] args) {
SpringApplication.run(DemoturbinecommonsApplication.class, args);
}
}</pre><p>
</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_zookeeper" href="#_spring_cloud_zookeeper"></a>Part&nbsp;X.&nbsp;Spring Cloud Zookeeper</h1></div></div></div><div class="partintro"><div></div><p>This project provides Zookeeper integrations for Spring Boot applications through
autoconfiguration and binding to the Spring Environment and other Spring programming model
idioms. With a few annotations, you can quickly enable and configure the common patterns
inside your application and build large distributed systems with Zookeeper based
components. The provided patterns include Service Discovery and Configuration. Integration
with Spring Cloud Netflix provides Intelligent Routing (Zuul), Client Side Load Balancing
(Ribbon), and Circuit Breaker (Hystrix).</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-zookeeper-install" href="#spring-cloud-zookeeper-install"></a>73.&nbsp;Install Zookeeper</h2></div></div></div><p>See the <a class="link" href="http://zookeeper.apache.org/doc/current/zookeeperStarted.html" target="_top">installation
documentation</a> for instructions on how to install Zookeeper.</p><p>Spring Cloud Zookeeper uses Apache Curator behind the scenes.
While Zookeeper 3.5.x is still considered "beta" by the Zookeeper development team,
the reality is that it is used in production by many users.
However, Zookeeper 3.4.x is also used in production.
Prior to Apache Curator 4.0, both versions of Zookeeper were supported via two versions of Apache Curator.
Starting with Curator 4.0 both versions of Zookeeper are supported via the same Curator libraries.</p><p>In case you are integrating with version 3.4 you need to change the Zookeeper dependency
that comes shipped with <code class="literal">curator</code>, and thus <code class="literal">spring-cloud-zookeeper</code>.
To do so simply exclude that dependency and add the 3.4.x version like shown below.</p><p><b>maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-zookeeper-all<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.apache.zookeeper<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>zookeeper<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.apache.zookeeper<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>zookeeper<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>3.4.12<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.slf4j<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>slf4j-log4j12<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>
</p><p><b>gradle.&nbsp;</b>
</p><pre class="programlisting">compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud:spring-cloud-starter-zookeeper-all'</span>) {
exclude group: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.apache.zookeeper'</span>, module: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'zookeeper'</span>
}
compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.apache.zookeeper:zookeeper:3.4.12'</span>) {
exclude group: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.slf4j'</span>, module: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'slf4j-log4j12'</span>
}</pre><p>
</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-zookeeper-discovery" href="#spring-cloud-zookeeper-discovery"></a>74.&nbsp;Service Discovery with Zookeeper</h2></div></div></div><p>Service Discovery is one of the key tenets of a microservice based architecture. Trying to
hand-configure each client or some form of convention can be difficult to do and can be
brittle. <a class="link" href="http://curator.apache.org" target="_top">Curator</a>(A Java library for Zookeeper) provides Service
Discovery through a <a class="link" href="http://curator.apache.org/curator-x-discovery/" target="_top">Service Discovery
Extension</a>. Spring Cloud Zookeeper uses this extension for service registration and
discovery.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_activating" href="#_activating"></a>74.1&nbsp;Activating</h2></div></div></div><p>Including a dependency on
<code class="literal">org.springframework.cloud:spring-cloud-starter-zookeeper-discovery</code> enables
autoconfiguration that sets up Spring Cloud Zookeeper Discovery.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>For web functionality, you still need to include
<code class="literal">org.springframework.boot:spring-boot-starter-web</code>.</p></td></tr></table></div><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p>When working with version 3.4 of Zookeeper you need to change
the way you include the dependency as described <a class="link" href="#spring-cloud-zookeeper-install" title="73.&nbsp;Install Zookeeper">here</a>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_with_zookeeper" href="#_registering_with_zookeeper"></a>74.2&nbsp;Registering with Zookeeper</h2></div></div></div><p>When a client registers with Zookeeper, it provides metadata (such as host and port, ID,
and name) about itself.</p><p>The following example shows a Zookeeper client:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@RequestMapping("/")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello world"</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).web(true).run(args);
}
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The preceding example is a normal Spring Boot application.</p></td></tr></table></div><p>If Zookeeper is located somewhere other than <code class="literal">localhost:2181</code>, the configuration must
provide the location of the server, as shown in the following example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting">spring:
cloud:
zookeeper:
connect-string: localhost:2181</pre><p>
</p><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p>If you use <a class="link" href="#spring-cloud-zookeeper-config" title="79.&nbsp;Distributed Configuration with Zookeeper">Spring Cloud Zookeeper Config</a>, the
values shown in the preceding example need to be in <code class="literal">bootstrap.yml</code> instead of
<code class="literal">application.yml</code>.</p></td></tr></table></div><p>The default service name, instance ID, and port (taken from the <code class="literal">Environment</code>) are
<code class="literal">${spring.application.name}</code>, the Spring Context ID, and <code class="literal">${server.port}</code>, respectively.</p><p>Having <code class="literal">spring-cloud-starter-zookeeper-discovery</code> on the classpath makes the app into both
a Zookeeper &#8220;service&#8221; (that is, it registers itself) and a &#8220;client&#8221; (that is, it can
query Zookeeper to locate other services).</p><p>If you would like to disable the Zookeeper Discovery Client, you can set
<code class="literal">spring.cloud.zookeeper.discovery.enabled</code> to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_the_discoveryclient_2" href="#_using_the_discoveryclient_2"></a>74.3&nbsp;Using the DiscoveryClient</h2></div></div></div><p>Spring Cloud has support for
<a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign" target="_top">Feign</a>
(a REST client builder) and
<a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-ribbon" target="_top">Spring
<code class="literal">RestTemplate</code></a>, using logical service names instead of physical URLs.</p><p>You can also use the <code class="literal">org.springframework.cloud.client.discovery.DiscoveryClient</code>, which
provides a simple API for discovery clients that is not specific to Netflix, as shown in
the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> DiscoveryClient discoveryClient;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String serviceUrl() {
List&lt;ServiceInstance&gt; list = discoveryClient.getInstances(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"STORES"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (list != null &amp;&amp; list.size() &gt; <span class="hl-number">0</span> ) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> list.get(<span class="hl-number">0</span>).getUri().toString();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
}</pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-zookeeper-netflix" href="#spring-cloud-zookeeper-netflix"></a>75.&nbsp;Using Spring Cloud Zookeeper with Spring Cloud Netflix Components</h2></div></div></div><p>Spring Cloud Netflix supplies useful tools that work regardless of which <code class="literal">DiscoveryClient</code>
implementation you use. Feign, Turbine, Ribbon, and Zuul all work with Spring Cloud
Zookeeper.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_ribbon_with_zookeeper" href="#_ribbon_with_zookeeper"></a>75.1&nbsp;Ribbon with Zookeeper</h2></div></div></div><p>Spring Cloud Zookeeper provides an implementation of Ribbon&#8217;s <code class="literal">ServerList</code>. When you use
the <code class="literal">spring-cloud-starter-zookeeper-discovery</code>, Ribbon is autoconfigured to use the
<code class="literal">ZookeeperServerList</code> by default.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-zookeeper-service-registry" href="#spring-cloud-zookeeper-service-registry"></a>76.&nbsp;Spring Cloud Zookeeper and Service Registry</h2></div></div></div><p>Spring Cloud Zookeeper implements the <code class="literal">ServiceRegistry</code> interface, letting developers
register arbitrary services in a programmatic way.</p><p>The <code class="literal">ServiceInstanceRegistration</code> class offers a <code class="literal">builder()</code> method to create a
<code class="literal">Registration</code> object that can be used by the <code class="literal">ServiceRegistry</code>, as shown in the following
example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> ZookeeperServiceRegistry serviceRegistry;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> registerThings() {
ZookeeperRegistration registration = ServiceInstanceRegistration.builder()
.defaultUriSpec()
.address(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"anyUrl"</span>)
.port(<span class="hl-number">10</span>)
.name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/a/b/c/d/anotherservice"</span>)
.build();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.serviceRegistry.register(registration);
}</pre><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_instance_status" href="#_instance_status"></a>76.1&nbsp;Instance Status</h2></div></div></div><p>Netflix Eureka supports having instances that are <code class="literal">OUT_OF_SERVICE</code> registered with the
server. These instances are not returned as active service instances. This is useful for
behaviors such as blue/green deployments. (Note that the Curator Service Discovery recipe
does not support this behavior.) Taking advantage of the flexible payload has let Spring
Cloud Zookeeper implement <code class="literal">OUT_OF_SERVICE</code> by updating some specific metadata and then
filtering on that metadata in the Ribbon <code class="literal">ZookeeperServerList</code>. The <code class="literal">ZookeeperServerList</code>
filters out all non-null instance statuses that do not equal <code class="literal">UP</code>. If the instance status
field is empty, it is considered to be <code class="literal">UP</code> for backwards compatibility. To change the
status of an instance, make a <code class="literal">POST</code> with <code class="literal">OUT_OF_SERVICE</code> to the <code class="literal">ServiceRegistry</code>
instance status actuator endpoint, as shown in the following example:</p><pre class="programlisting">$ http POST http://localhost:8081/service-registry status=OUT_OF_SERVICE</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The preceding example uses the <code class="literal">http</code> command from <a class="link" href="https://httpie.org" target="_top">https://httpie.org</a>.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-zookeeper-dependencies" href="#spring-cloud-zookeeper-dependencies"></a>77.&nbsp;Zookeeper Dependencies</h2></div></div></div><p>The following topics cover how to work with Spring Cloud Zookeeper dependencies:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#spring-cloud-zookeeper-dependencies-using" title="77.1&nbsp;Using the Zookeeper Dependencies">Section&nbsp;77.1, &#8220;Using the Zookeeper Dependencies&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-zookeeper-dependencies-activating" title="77.2&nbsp;Activating Zookeeper Dependencies">Section&nbsp;77.2, &#8220;Activating Zookeeper Dependencies&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-zookeeper-dependencies-setting-up" title="77.3&nbsp;Setting up Zookeeper Dependencies">Section&nbsp;77.3, &#8220;Setting up Zookeeper Dependencies&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-zookeeper-dependencies-configuring" title="77.4&nbsp;Configuring Spring Cloud Zookeeper Dependencies">Section&nbsp;77.4, &#8220;Configuring Spring Cloud Zookeeper Dependencies&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-zookeeper-dependencies-using" href="#spring-cloud-zookeeper-dependencies-using"></a>77.1&nbsp;Using the Zookeeper Dependencies</h2></div></div></div><p>Spring Cloud Zookeeper gives you a possibility to provide dependencies of your application
as properties. As dependencies, you can understand other applications that are registered
in Zookeeper and which you would like to call through
<a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-feign" target="_top">Feign</a>
(a REST client builder) and <a class="link" href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/docs/src/main/asciidoc/spring-cloud-netflix.adoc#spring-cloud-ribbon" target="_top">Spring <code class="literal">RestTemplate</code></a>.</p><p>You can also use the Zookeeper Dependency Watchers functionality to control and monitor
the state of your dependencies.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-zookeeper-dependencies-activating" href="#spring-cloud-zookeeper-dependencies-activating"></a>77.2&nbsp;Activating Zookeeper Dependencies</h2></div></div></div><p>Including a dependency on
<code class="literal">org.springframework.cloud:spring-cloud-starter-zookeeper-discovery</code> enables
autoconfiguration that sets up Spring Cloud Zookeeper Dependencies. Even if you provide
the dependencies in your properties, you can turn off the dependencies. To do so, set the
<code class="literal">spring.cloud.zookeeper.dependency.enabled</code> property to false (it defaults to <code class="literal">true</code>).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-zookeeper-dependencies-setting-up" href="#spring-cloud-zookeeper-dependencies-setting-up"></a>77.3&nbsp;Setting up Zookeeper Dependencies</h2></div></div></div><p>Consider the following example of dependency representation:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting">spring.application.name: yourServiceName
spring.cloud.zookeeper:
dependencies:
newsletter:
path: /path/where/newsletter/has/registered/in/zookeeper
loadBalancerType: ROUND_ROBIN
contentTypeTemplate: application/vnd.newsletter.$version+json
version: v1
headers:
header1:
- value1
header2:
- value2
required: false
stubs: org.springframework:foo:stubs
mailing:
path: /path/where/mailing/has/registered/in/zookeeper
loadBalancerType: ROUND_ROBIN
contentTypeTemplate: application/vnd.mailing.$version+json
version: v1
required: true</pre><p>
</p><p>The next few sections go through each part of the dependency one by one. The root property
name is <code class="literal">spring.cloud.zookeeper.dependencies</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-zookeeper-dependencies-setting-up-aliases" href="#spring-cloud-zookeeper-dependencies-setting-up-aliases"></a>77.3.1&nbsp;Aliases</h3></div></div></div><p>Below the root property you have to represent each dependency as an alias. This is due to
the constraints of Ribbon, which requires that the application ID be placed in the URL.
Consequently, you cannot pass any complex path, suchas <code class="literal">/myApp/myRoute/name</code>). The alias
is the name you use instead of the <code class="literal">serviceId</code> for <code class="literal">DiscoveryClient</code>, <code class="literal">Feign</code>, or
<code class="literal">RestTemplate</code>.</p><p>In the previous examples, the aliases are <code class="literal">newsletter</code> and <code class="literal">mailing</code>. The following
example shows Feign usage with a <code class="literal">newsletter</code> alias:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@FeignClient("newsletter")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> NewsletterService {
<em><span class="hl-annotation" style="color: gray">@RequestMapping(method = RequestMethod.GET, value = "/newsletter")</span></em>
String getNewsletters();
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_path" href="#_path"></a>77.3.2&nbsp;Path</h3></div></div></div><p>The path is represented by the <code class="literal">path</code> YAML property and is the path under which the
dependency is registered under Zookeeper. As described in the
<a class="link" href="#spring-cloud-zookeeper-dependencies-setting-up-aliases" title="77.3.1&nbsp;Aliases">previous section</a>, Ribbon
operates on URLs. As a result, this path is not compliant with its requirement.
That is why Spring Cloud Zookeeper maps the alias to the proper path.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_load_balancer_type" href="#_load_balancer_type"></a>77.3.3&nbsp;Load Balancer Type</h3></div></div></div><p>The load balancer type is represented by <code class="literal">loadBalancerType</code> YAML property.</p><p>If you know what kind of load-balancing strategy has to be applied when calling this
particular dependency, you can provide it in the YAML file, and it is automatically
applied. You can choose one of the following load balancing strategies:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">STICKY: Once chosen, the instance is always called.</li><li class="listitem">RANDOM: Picks an instance randomly.</li><li class="listitem">ROUND_ROBIN: Iterates over instances over and over again.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="__literal_content_type_literal_template_and_version" href="#__literal_content_type_literal_template_and_version"></a>77.3.4&nbsp;<code class="literal">Content-Type</code> Template and Version</h3></div></div></div><p>The <code class="literal">Content-Type</code> template and version are represented by the <code class="literal">contentTypeTemplate</code> and
<code class="literal">version</code> YAML properties.</p><p>If you version your API in the <code class="literal">Content-Type</code> header, you do not want to add this header
to each of your requests. Also, if you want to call a new version of the API, you do not
want to roam around your code to bump up the API version. That is why you can provide a
<code class="literal">contentTypeTemplate</code> with a special <code class="literal">$version</code> placeholder. That placeholder will be filled by the value of the
<code class="literal">version</code> YAML property. Consider the following example of a <code class="literal">contentTypeTemplate</code>:</p><pre class="screen">application/vnd.newsletter.$version+json</pre><p>Further consider the following <code class="literal">version</code>:</p><pre class="screen">v1</pre><p>The combination of <code class="literal">contentTypeTemplate</code> and version results in the creation of a
<code class="literal">Content-Type</code> header for each request, as follows:</p><pre class="screen">application/vnd.newsletter.v1+json</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_default_headers" href="#_default_headers"></a>77.3.5&nbsp;Default Headers</h3></div></div></div><p>Default headers are represented by the <code class="literal">headers</code> map in YAML.</p><p>Sometimes, each call to a dependency requires setting up of some default headers. To not
do that in code, you can set them up in the YAML file, as shown in the following example
<code class="literal">headers</code> section:</p><pre class="programlisting">headers:
Accept:
- text/html
- application/xhtml+xml
Cache-Control:
- no-cache</pre><p>That <code class="literal">headers</code> section results in adding the <code class="literal">Accept</code> and <code class="literal">Cache-Control</code> headers with
appropriate list of values in your HTTP request.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_required_dependencies" href="#_required_dependencies"></a>77.3.6&nbsp;Required Dependencies</h3></div></div></div><p>Required dependencies are represented by <code class="literal">required</code> property in YAML.</p><p>If one of your dependencies is required to be up when your application boots, you can set
the <code class="literal">required: true</code> property in the YAML file.</p><p>If your application cannot localize the required dependency during boot time, it throws an
exception, and the Spring Context fails to set up. In other words, your application cannot
start if the required dependency is not registered in Zookeeper.</p><p>You can read more about Spring Cloud Zookeeper Presence Checker
<a class="link" href="#spring-cloud-zookeeper-dependency-watcher-presence-checker" title="78.3&nbsp;Using the Presence Checker">later in this document</a>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stubs" href="#_stubs"></a>77.3.7&nbsp;Stubs</h3></div></div></div><p>You can provide a colon-separated path to the JAR containing stubs of the dependency, as
shown in the following example:</p><p><code class="literal">stubs: org.springframework:myApp:stubs</code></p><p>where:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">org.springframework</code> is the <code class="literal">groupId</code>.</li><li class="listitem"><code class="literal">myApp</code> is the <code class="literal">artifactId</code>.</li><li class="listitem"><code class="literal">stubs</code> is the classifier. (Note that <code class="literal">stubs</code> is the default value.)</li></ul></div><p>Because <code class="literal">stubs</code> is the default classifier, the preceding example is equal to the following
example:</p><p><code class="literal">stubs: org.springframework:myApp</code></p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-zookeeper-dependencies-configuring" href="#spring-cloud-zookeeper-dependencies-configuring"></a>77.4&nbsp;Configuring Spring Cloud Zookeeper Dependencies</h2></div></div></div><p>You can set the following properties to enable or disable parts of Zookeeper Dependencies
functionalities:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">spring.cloud.zookeeper.dependencies</code>: If you do not set this property, you cannot use
Zookeeper Dependencies.</li><li class="listitem"><code class="literal">spring.cloud.zookeeper.dependency.ribbon.enabled</code> (enabled by default): Ribbon requires
either explicit global configuration or a particular one for a dependency. By turning on
this property, runtime load balancing strategy resolution is possible, and you can use the
<code class="literal">loadBalancerType</code> section of the Zookeeper Dependencies. The configuration that needs
this property has an implementation of <code class="literal">LoadBalancerClient</code> that delegates to the
<code class="literal">ILoadBalancer</code> presented in the next bullet.</li><li class="listitem"><code class="literal">spring.cloud.zookeeper.dependency.ribbon.loadbalancer</code> (enabled by default): Thanks to
this property, the custom <code class="literal">ILoadBalancer</code> knows that the part of the URI passed to Ribbon
might actually be the alias that has to be resolved to a proper path in Zookeeper. Without
this property, you cannot register applications under nested paths.</li><li class="listitem"><code class="literal">spring.cloud.zookeeper.dependency.headers.enabled</code> (enabled by default): This property
registers a <code class="literal">RibbonClient</code> that automatically appends appropriate headers and content
types with their versions, as presented in the Dependency configuration. Without this
setting, those two parameters do not work.</li><li class="listitem"><code class="literal">spring.cloud.zookeeper.dependency.resttemplate.enabled</code> (enabled by default): When
enabled, this property modifies the request headers of a <code class="literal">@LoadBalanced</code>-annotated
<code class="literal">RestTemplate</code> such that it passes headers and content type with the version set in
dependency configuration. Without this setting, those two parameters do not work.</li></ul></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-zookeeper-dependency-watcher" href="#spring-cloud-zookeeper-dependency-watcher"></a>78.&nbsp;Spring Cloud Zookeeper Dependency Watcher</h2></div></div></div><p>The Dependency Watcher mechanism lets you register listeners to your dependencies. The
functionality is, in fact, an implementation of the <code class="literal">Observator</code> pattern. When a
dependency changes, its state (to either UP or DOWN), some custom logic can be applied.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_activating_2" href="#_activating_2"></a>78.1&nbsp;Activating</h2></div></div></div><p>Spring Cloud Zookeeper Dependencies functionality needs to be enabled for you to use the
Dependency Watcher mechanism.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_a_listener" href="#_registering_a_listener"></a>78.2&nbsp;Registering a Listener</h2></div></div></div><p>To register a listener, you must implement an interface called
<code class="literal">org.springframework.cloud.zookeeper.discovery.watcher.DependencyWatcherListener</code> and
register it as a bean. The interface gives you one method:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> stateChanged(String dependencyName, DependencyState newState);</pre><p>If you want to register a listener for a particular dependency, the <code class="literal">dependencyName</code> would
be the discriminator for your concrete implementation. <code class="literal">newState</code> provides you with
information about whether your dependency has changed to <code class="literal">CONNECTED</code> or <code class="literal">DISCONNECTED</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="spring-cloud-zookeeper-dependency-watcher-presence-checker" href="#spring-cloud-zookeeper-dependency-watcher-presence-checker"></a>78.3&nbsp;Using the Presence Checker</h2></div></div></div><p>Bound with the Dependency Watcher is the functionality called Presence Checker. It lets
you provide custom behavior when your application boots, to react according to the state
of your dependencies.</p><p>The default implementation of the abstract
<code class="literal">org.springframework.cloud.zookeeper.discovery.watcher.presence.DependencyPresenceOnStartupVerifier</code>
class is the
<code class="literal">org.springframework.cloud.zookeeper.discovery.watcher.presence.DefaultDependencyPresenceOnStartupVerifier</code>,
which works in the following way.</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">If the dependency is marked us <code class="literal">required</code> and is not in Zookeeper, when your application
boots, it throws an exception and shuts down.</li><li class="listitem">If the dependency is not <code class="literal">required</code>, the
<code class="literal">org.springframework.cloud.zookeeper.discovery.watcher.presence.LogMissingDependencyChecker</code>
logs that the dependency is missing at the <code class="literal">WARN</code> level.</li></ol></div><p>Because the <code class="literal">DefaultDependencyPresenceOnStartupVerifier</code> is registered only when there is
no bean of type <code class="literal">DependencyPresenceOnStartupVerifier</code>, this functionality can be
overridden.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="spring-cloud-zookeeper-config" href="#spring-cloud-zookeeper-config"></a>79.&nbsp;Distributed Configuration with Zookeeper</h2></div></div></div><p>Zookeeper provides a
<a class="link" href="http://zookeeper.apache.org/doc/current/zookeeperOver.html#sc_dataModelNameSpace" target="_top">hierarchical namespace</a>
that lets clients store arbitrary data, such as configuration data. Spring Cloud Zookeeper
Config is an alternative to the
<a class="link" href="https://github.com/spring-cloud/spring-cloud-config" target="_top">Config Server and Client</a>.
Configuration is loaded into the Spring Environment during the special &#8220;bootstrap&#8221;
phase. Configuration is stored in the <code class="literal">/config</code> namespace by default. Multiple
<code class="literal">PropertySource</code> instances are created, based on the application&#8217;s name and the active
profiles, to mimic the Spring Cloud Config order of resolving properties. For example, an
application with a name of <code class="literal">testApp</code> and with the <code class="literal">dev</code> profile has the following property
sources created for it:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">config/testApp,dev</code></li><li class="listitem"><code class="literal">config/testApp</code></li><li class="listitem"><code class="literal">config/application,dev</code></li><li class="listitem"><code class="literal">config/application</code></li></ul></div><p>The most specific property source is at the top, with the least specific at the bottom.
Properties in the <code class="literal">config/application</code> namespace apply to all applications that use
zookeeper for configuration. Properties in the <code class="literal">config/testApp</code> namespace are available
only to the instances of the service named <code class="literal">testApp</code>.</p><p>Configuration is currently read on startup of the application. Sending a HTTP <code class="literal">POST</code>
request to <code class="literal">/refresh</code> causes the configuration to be reloaded. Watching the configuration
namespace (which Zookeeper supports) is not currently implemented.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_activating_3" href="#_activating_3"></a>79.1&nbsp;Activating</h2></div></div></div><p>Including a dependency on
<code class="literal">org.springframework.cloud:spring-cloud-starter-zookeeper-config</code> enables
autoconfiguration that sets up Spring Cloud Zookeeper Config.</p><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p>When working with version 3.4 of Zookeeper you need to change
the way you include the dependency as described <a class="link" href="#spring-cloud-zookeeper-install" title="73.&nbsp;Install Zookeeper">here</a>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customizing_2" href="#_customizing_2"></a>79.2&nbsp;Customizing</h2></div></div></div><p>Zookeeper Config may be customized by setting the following properties:</p><p><b>bootstrap.yml.&nbsp;</b>
</p><pre class="programlisting">spring:
cloud:
zookeeper:
config:
enabled: true
root: configuration
defaultContext: apps
profileSeparator: '::'</pre><p>
</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code>: Setting this value to <code class="literal">false</code> disables Zookeeper Config.</li><li class="listitem"><code class="literal">root</code>: Sets the base namespace for configuration values.</li><li class="listitem"><code class="literal">defaultContext</code>: Sets the name used by all applications.</li><li class="listitem"><code class="literal">profileSeparator</code>: Sets the value of the separator used to separate the profile name in
property sources with profiles.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_access_control_lists_acls" href="#_access_control_lists_acls"></a>79.3&nbsp;Access Control Lists (ACLs)</h2></div></div></div><p>You can add authentication information for Zookeeper ACLs by calling the <code class="literal">addAuthInfo</code>
method of a <code class="literal">CuratorFramework</code> bean. One way to accomplish this is to provide your own
<code class="literal">CuratorFramework</code> bean, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@BoostrapConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomCuratorFrameworkConfig {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> CuratorFramework curatorFramework() {
CuratorFramework curator = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> CuratorFramework();
curator.addAuthInfo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"digest"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"user:password"</span>.getBytes());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> curator;
}
}</pre><p>Consult
<a class="link" href="https://github.com/spring-cloud/spring-cloud-zookeeper/blob/master/spring-cloud-zookeeper-core/src/main/java/org/springframework/cloud/zookeeper/ZookeeperAutoConfiguration.java" target="_top">the ZookeeperAutoConfiguration class</a>
to see how the <code class="literal">CuratorFramework</code> bean&#8217;s default configuration.</p><p>Alternatively, you can add your credentials from a class that depends on the existing
<code class="literal">CuratorFramework</code> bean, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@BoostrapConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> DefaultCuratorFrameworkConfig {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ZookeeperConfig(CuratorFramework curator) {
curator.addAuthInfo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"digest"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"user:password"</span>.getBytes());
}
}</pre><p>The creation of this bean must occur during the boostrapping phase. You can register
configuration classes to run during this phase by annotating them with
<code class="literal">@BootstrapConfiguration</code> and including them in a comma-separated list that you set as the
value of the <code class="literal">org.springframework.cloud.bootstrap.BootstrapConfiguration</code> property in the
<code class="literal">resources/META-INF/spring.factories</code> file, as shown in the following example:</p><p><b>resources/META-INF/spring.factories.&nbsp;</b>
</p><pre class="screen">org.springframework.cloud.bootstrap.BootstrapConfiguration=\
my.project.CustomCuratorFrameworkConfig,\
my.project.DefaultCuratorFrameworkConfig</pre><p>
</p></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_security" href="#_spring_cloud_security"></a>Part&nbsp;XI.&nbsp;Spring Cloud Security</h1></div></div></div><div class="partintro"><div></div><p>Spring Cloud Security offers a set of primitives for building secure
applications and services with minimum fuss. A declarative model which
can be heavily configured externally (or centrally) lends itself to
the implementation of large systems of co-operating, remote components,
usually with a central indentity management service. It is also extremely
easy to use in a service platform like Cloud Foundry. Building on
Spring Boot and Spring Security OAuth2 we can quickly create systems that
implement common patterns like single sign on, token relay and token
exchange.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Spring Cloud is released under the non-restrictive Apache 2.0 license. If you would like to contribute to this section of the documentation or if you find an error, please find the source code and issue trackers in the project at <a class="link" href="https://github.com/spring-cloud/spring-cloud-security/tree/master/src/main/asciidoc" target="_top">github</a>.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_quickstart" href="#_quickstart"></a>80.&nbsp;Quickstart</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_oauth2_single_sign_on" href="#_oauth2_single_sign_on"></a>80.1&nbsp;OAuth2 Single Sign On</h2></div></div></div><p>Here&#8217;s a Spring Cloud "Hello World" app with HTTP Basic
authentication and a single user account:</p><p><b>app.groovy.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Grab('spring-boot-starter-security')</span></em>
<em><span class="hl-annotation" style="color: gray">@Controller</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@RequestMapping('/')</span></em>
String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Hello World'</span>
}
}</pre><p>
</p><p>You can run it with <code class="literal">spring run app.groovy</code> and watch the logs for the password (username is "user"). So far this is just the default for a Spring Boot app.</p><p>Here&#8217;s a Spring Cloud app with OAuth2 SSO:</p><p><b>app.groovy.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Controller</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableOAuth2Sso</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@RequestMapping('/')</span></em>
String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Hello World'</span>
}
}</pre><p>
</p><p>Spot the difference? This app will actually behave exactly the same as
the previous one, because it doesn&#8217;t know it&#8217;s OAuth2 credentals
yet.</p><p>You can register an app in github quite easily, so try that if you
want a production app on your own domain. If you are happy to test on
localhost:8080, then set up these properties in your application
configuration:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">security</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> oauth2</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> client</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> clientId</span>: bd1c0a783ccdd1c9b9e4
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> clientSecret</span>: <span class="hl-number">1</span>a9030fbca47a5b2c28e92f19050bb77824b5ad1
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> accessTokenUri</span>: https://github.com/login/oauth/access_token
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> userAuthorizationUri</span>: https://github.com/login/oauth/authorize
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> clientAuthenticationScheme</span>: form
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> resource</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> userInfoUri</span>: https://api.github.com/user
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> preferTokenInfo</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span></pre><p>
</p><p>run the app above and it will redirect to github for authorization. If
you are already signed into github you won&#8217;t even notice that it has
authenticated. These credentials will only work if your app is
running on port 8080.</p><p>To limit the scope that the client asks for when it obtains an access token
you can set <code class="literal">security.oauth2.client.scope</code> (comma separated or an array in YAML). By
default the scope is empty and it is up to to Authorization Server to
decide what the defaults should be, usually depending on the settings in
the client registration that it holds.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The examples above are all Groovy scripts. If you want to write the
same code in Java (or Groovy) you need to add Spring Security OAuth2
to the classpath (e.g. see the
<a class="link" href="https://github.com/spring-cloud-samples/sso" target="_top">sample here</a>).</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_oauth2_protected_resource" href="#_oauth2_protected_resource"></a>80.2&nbsp;OAuth2 Protected Resource</h2></div></div></div><p>You want to protect an API resource with an OAuth2 token? Here&#8217;s a
simple example (paired with the client above):</p><p><b>app.groovy.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Grab('spring-cloud-starter-security')</span></em>
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableResourceServer</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@RequestMapping('/')</span></em>
def home() {
[message: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Hello World'</span>]
}
}</pre><p>
</p><p>and</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">security</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> oauth2</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> resource</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> userInfoUri</span>: https://api.github.com/user
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> preferTokenInfo</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span></pre><p>
</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_more_detail" href="#_more_detail"></a>81.&nbsp;More Detail</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_single_sign_on" href="#_single_sign_on"></a>81.1&nbsp;Single Sign On</h2></div></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>All of the OAuth2 SSO and resource server features moved to Spring Boot
in version 1.3. You can find documentation in the
<a class="link" href="http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/" target="_top">Spring Boot user guide</a>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_token_relay" href="#_token_relay"></a>81.2&nbsp;Token Relay</h2></div></div></div><p>A Token Relay is where an OAuth2 consumer acts as a Client and
forwards the incoming token to outgoing resource requests. The
consumer can be a pure Client (like an SSO application) or a Resource
Server.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_client_token_relay" href="#_client_token_relay"></a>81.2.1&nbsp;Client Token Relay</h3></div></div></div><p>If your app is a user facing OAuth2 client (i.e. has declared
<code class="literal">@EnableOAuth2Sso</code> or <code class="literal">@EnableOAuth2Client</code>) then it has an
<code class="literal">OAuth2ClientContext</code> in request scope from Spring Boot. You can
create your own <code class="literal">OAuth2RestTemplate</code> from this context and an
autowired <code class="literal">OAuth2ProtectedResourceDetails</code>, and then the context will
always forward the access token downstream, also refreshing the access
token automatically if it expires. (These are features of Spring
Security and Spring Boot.)</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Spring Boot (1.4.1) does not create an
<code class="literal">OAuth2ProtectedResourceDetails</code> automatically if you are using
<code class="literal">client_credentials</code> tokens. In that case you need to create your own
<code class="literal">ClientCredentialsResourceDetails</code> and configure it with
<code class="literal">@ConfigurationProperties("security.oauth2.client")</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_client_token_relay_in_zuul_proxy" href="#_client_token_relay_in_zuul_proxy"></a>81.2.2&nbsp;Client Token Relay in Zuul Proxy</h3></div></div></div><p>If your app also has a
<a class="link" href="http://cloud.spring.io/spring-cloud.html#netflix-zuul-reverse-proxy" target="_top">Spring
Cloud Zuul</a> embedded reverse proxy (using <code class="literal">@EnableZuulProxy</code>) then you
can ask it to forward OAuth2 access tokens downstream to the services
it is proxying. Thus the SSO app above can be enhanced simply like
this:</p><p><b>app.groovy.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Controller</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableOAuth2Sso</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableZuulProxy</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
}</pre><p>
</p><p>and it will (in addition to logging the user in and grabbing a token)
pass the authentication token downstream to the <code class="literal">/proxy/*</code>
services. If those services are implemented with
<code class="literal">@EnableResourceServer</code> then they will get a valid token in the
correct header.</p><p>How does it work? The <code class="literal">@EnableOAuth2Sso</code> annotation pulls in
<code class="literal">spring-cloud-starter-security</code> (which you could do manually in a
traditional app), and that in turn triggers some autoconfiguration for
a <code class="literal">ZuulFilter</code>, which itself is activated because Zuul is on the
classpath (via <code class="literal">@EnableZuulProxy</code>). The
<a class="link" href="https://github.com/spring-cloud/spring-cloud-security/tree/master/src/main/java/org/springframework/cloud/security/oauth2/proxy/OAuth2TokenRelayFilter.java" target="_top">filter</a>
just extracts an access token from the currently authenticated user,
and puts it in a request header for the downstream requests.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_resource_server_token_relay" href="#_resource_server_token_relay"></a>81.2.3&nbsp;Resource Server Token Relay</h3></div></div></div><p>If your app has <code class="literal">@EnableResourceServer</code> you might want to relay the
incoming token downstream to other services. If you use a
<code class="literal">RestTemplate</code> to contact the downstream services then this is just a
matter of how to create the template with the right context.</p><p>If your service uses <code class="literal">UserInfoTokenServices</code> to authenticate incoming
tokens (i.e. it is using the <code class="literal">security.oauth2.user-info-uri</code>
configuration), then you can simply create an <code class="literal">OAuth2RestTemplate</code>
using an autowired <code class="literal">OAuth2ClientContext</code> (it will be populated by the
authentication process before it hits the backend code). Equivalently
(with Spring Boot 1.4), you could inject a
<code class="literal">UserInfoRestTemplateFactory</code> and grab its <code class="literal">OAuth2RestTemplate</code> in
your configuration. For example:</p><p><b>MyConfiguration.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> factory.getUserInfoRestTemplate();
}</pre><p>
</p><p>This rest template will then have the same <code class="literal">OAuth2ClientContext</code>
(request-scoped) that is used by the authentication filter, so you can
use it to send requests with the same access token.</p><p>If your app is not using <code class="literal">UserInfoTokenServices</code> but is still a client
(i.e. it declares <code class="literal">@EnableOAuth2Client</code> or <code class="literal">@EnableOAuth2Sso</code>), then
with Spring Security Cloud any <code class="literal">OAuth2RestOperations</code> that the user
creates from an <code class="literal">@Autowired</code> <code class="literal">@OAuth2Context</code> will also forward
tokens. This feature is implemented by default as an MVC handler
interceptor, so it only works in Spring MVC. If you are not using MVC
you could use a custom filter or AOP interceptor wrapping an
<code class="literal">AccessTokenContextRelay</code> to provide the same feature.</p><p>Here&#8217;s a basic
example showing the use of an autowired rest template created
elsewhere ("foo.com" is a Resource Server accepting the same tokens as
the surrounding app):</p><p><b>MyController.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> OAuth2RestOperations restTemplate;
<em><span class="hl-annotation" style="color: gray">@RequestMapping("/relay")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String relay() {
ResponseEntity&lt;String&gt; response =
restTemplate.getForEntity(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://foo.com/bar"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Success! ("</span> + response.getBody() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">")"</span>;
}</pre><p>
</p><p>If you don&#8217;t want to forward tokens (and that is a valid
choice, since you might want to act as yourself, rather than the
client that sent you the token), then you only need to create your own
<code class="literal">OAuth2Context</code> instead of autowiring the default one.</p><p>Feign clients will also pick up an interceptor that uses the
<code class="literal">OAuth2ClientContext</code> if it is available, so they should also do a
token relay anywhere where a <code class="literal">RestTemplate</code> would.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_configuring_authentication_downstream_of_a_zuul_proxy" href="#_configuring_authentication_downstream_of_a_zuul_proxy"></a>82.&nbsp;Configuring Authentication Downstream of a Zuul Proxy</h2></div></div></div><p>You can control the authorization behaviour downstream of an
<code class="literal">@EnableZuulProxy</code> through the <code class="literal">proxy.auth.*</code> settings. Example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">proxy</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> auth</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> customers</span>: oauth2
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stores</span>: passthru
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> recommendations</span>: none</pre><p>
</p><p>In this example the "customers" service gets an OAuth2 token relay,
the "stores" service gets a passthrough (the authorization header is
just passed downstream), and the "recommendations" service has its
authorization header removed. The default behaviour is to do a token
relay if there is a token available, and passthru otherwise.</p><p>See
<a class="link" href="https://github.com/spring-cloud/spring-cloud-security/tree/master/src/main/java/org/springframework/cloud/security/oauth2/proxy/ProxyAuthenticationProperties" target="_top">
ProxyAuthenticationProperties</a> for full details.</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_for_cloud_foundry" href="#_spring_cloud_for_cloud_foundry"></a>Part&nbsp;XII.&nbsp;Spring Cloud for Cloud Foundry</h1></div></div></div><div class="partintro"><div></div><p>Spring Cloud for Cloudfoundry makes it easy to run
<a class="link" href="https://github.com/spring-cloud" target="_top">Spring Cloud</a> apps in
<a class="link" href="https://github.com/cloudfoundry" target="_top">Cloud Foundry</a> (the Platform as a
Service). Cloud Foundry has the notion of a "service", which is
middlware that you "bind" to an app, essentially providing it with an
environment variable containing credentials (e.g. the location and
username to use for the service).</p><p>The <code class="literal">spring-cloud-cloudfoundry-commons</code> module configures the
Reactor-based Cloud Foundry Java client, v 3.0, and can be used standalone.</p><p>The <code class="literal">spring-cloud-cloudfoundry-web</code> project provides basic support for
some enhanced features of webapps in Cloud Foundry: binding
automatically to single-sign-on services and optionally enabling
sticky routing for discovery.</p><p>The <code class="literal">spring-cloud-cloudfoundry-discovery</code> project provides an
implementation of Spring Cloud Commons <code class="literal">DiscoveryClient</code> so you can
<code class="literal">@EnableDiscoveryClient</code> and provide your credentials as
<code class="literal">spring.cloud.cloudfoundry.discovery.[username,password]</code> (also <code class="literal">*.url</code> if you are not connecting to <a class="link" href="https://run.pivotal.io" target="_top">Pivotal Web Services</a>) and then you
can use the <code class="literal">DiscoveryClient</code> directly or via a <code class="literal">LoadBalancerClient</code>.</p><p>The first time you use it the discovery client might be slow owing to
the fact that it has to get an access token from Cloud Foundry.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_discovery" href="#_discovery"></a>83.&nbsp;Discovery</h2></div></div></div><p>Here&#8217;s a Spring Cloud app with Cloud Foundry discovery:</p><p><b>app.groovy.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Grab('org.springframework.cloud:spring-cloud-cloudfoundry')</span></em>
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableDiscoveryClient</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
DiscoveryClient client
<em><span class="hl-annotation" style="color: gray">@RequestMapping('/')</span></em>
String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Hello from '</span> + client.getLocalServiceInstance()
}
}</pre><p>
</p><p>If you run it without any service bindings:</p><pre class="screen">$ spring jar app.jar app.groovy
$ cf push -p app.jar</pre><p>It will show its app name in the home page.</p><p>The <code class="literal">DiscoveryClient</code> can lists all the apps in a space, according to
the credentials it is authenticated with, where the space defaults to
the one the client is running in (if any). If neither org nor space
are configured, they default per the user&#8217;s profile in Cloud Foundry.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_single_sign_on_2" href="#_single_sign_on_2"></a>84.&nbsp;Single Sign On</h2></div></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>All of the OAuth2 SSO and resource server features moved to Spring Boot
in version 1.3. You can find documentation in the
<a class="link" href="http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/" target="_top">Spring Boot user guide</a>.</p></td></tr></table></div><p>This project provides automatic binding from CloudFoundry service
credentials to the Spring Boot features. If you have a CloudFoundry
service called "sso", for instance, with credentials containing
"client_id", "client_secret" and "auth_domain", it will bind
automatically to the Spring OAuth2 client that you enable with
<code class="literal">@EnableOAuth2Sso</code> (from Spring Boot). The name of the service can be
parameterized using <code class="literal">spring.oauth2.sso.serviceId</code>.</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_contract" href="#_spring_cloud_contract"></a>Part&nbsp;XIII.&nbsp;Spring Cloud Contract</h1></div></div></div><div class="partintro"><div></div><p><span class="emphasis"><em>Documentation Authors: Adam Dudczak, Mathias D&uuml;sterh&ouml;ft, Marcin Grzejszczak, Dennis Kieselhorst, Jakub Kubry&#324;ski, Karol Lassak,
Olga Maciaszek-Sharma, Mariusz Smyku&#322;a, Dave Syer, Jay Bryant</em></span></p><p>Greenwich.M3</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_contract_2" href="#_spring_cloud_contract_2"></a>85.&nbsp;Spring Cloud Contract</h2></div></div></div><p>You need confidence when pushing new features to a new application or service in a
distributed system. This project provides support for Consumer Driven Contracts and
service schemas in Spring applications (for both HTTP and message-based interactions),
covering a range of options for writing tests, publishing them as assets, and asserting
that a contract is kept by producers and consumers.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_contract_verifier_introduction" href="#_spring_cloud_contract_verifier_introduction"></a>86.&nbsp;Spring Cloud Contract Verifier Introduction</h2></div></div></div><p>Spring Cloud Contract Verifier enables Consumer Driven Contract (CDC) development of
JVM-based applications. It moves TDD to the level of software architecture.</p><p>Spring Cloud Contract Verifier ships with <span class="emphasis"><em>Contract Definition Language</em></span> (CDL). Contract
definitions are used to produce the following resources:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">JSON stub definitions to be used by WireMock when doing integration testing on the
client code (<span class="emphasis"><em>client tests</em></span>). Test code must still be written by hand, and test data is
produced by Spring Cloud Contract Verifier.</li><li class="listitem">Messaging routes, if you&#8217;re using a messaging service. We integrate with Spring
Integration, Spring Cloud Stream, Spring AMQP, and Apache Camel. You can also set your
own integrations.</li><li class="listitem">Acceptance tests (in JUnit 4, JUnit 5 or Spock) are used to verify if server-side implementation
of the API is compliant with the contract (<span class="emphasis"><em>server tests</em></span>). A full test is generated by
Spring Cloud Contract Verifier.</li></ul></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_history" href="#_history"></a>86.1&nbsp;History</h2></div></div></div><p>Before becoming Spring Cloud Contract, this project was called <a class="link" href="https://github.com/Codearte/accurest" target="_top">Accurest</a>.
It was created by <a class="link" href="https://twitter.com/mgrzejszczak" target="_top">Marcin Grzejszczak</a> and <a class="link" href="https://twitter.com/jkubrynski" target="_top">Jakub Kubrynski</a>
from (<a class="link" href="http://codearte.io" target="_top">codearte.io</a>.</p><p>The <code class="literal">0.1.0</code> release took place on 26 Jan 2015 and it became stable with <code class="literal">1.0.0</code> release on 29 Feb 2016.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_why_a_contract_verifier" href="#_why_a_contract_verifier"></a>86.2&nbsp;Why a Contract Verifier?</h2></div></div></div><p>Assume that we have a system consisting of multiple microservices:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/docs/src/main/asciidoc/images/Deps.png" alt="Microservices Architecture"></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_testing_issues" href="#_testing_issues"></a>86.2.1&nbsp;Testing issues</h3></div></div></div><p>If we wanted to test the application in top left corner to determine whether it can
communicate with other services, we could do one of two things:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Deploy all microservices and perform end-to-end tests.</li><li class="listitem">Mock other microservices in unit/integration tests.</li></ul></div><p>Both have their advantages but also a lot of disadvantages.</p><p><span class="strong"><strong>Deploy all microservices and perform end to end tests</strong></span></p><p>Advantages:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Simulates production.</li><li class="listitem">Tests real communication between services.</li></ul></div><p>Disadvantages:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">To test one microservice, we have to deploy 6 microservices, a couple of databases,
etc.</li><li class="listitem">The environment where the tests run is locked for a single suite of tests (nobody else
would be able to run the tests in the meantime).</li><li class="listitem">They take a long time to run.</li><li class="listitem">The feedback comes very late in the process.</li><li class="listitem">They are extremely hard to debug.</li></ul></div><p><span class="strong"><strong>Mock other microservices in unit/integration tests</strong></span></p><p>Advantages:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">They provide very fast feedback.</li><li class="listitem">They have no infrastructure requirements.</li></ul></div><p>Disadvantages:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The implementor of the service creates stubs that might have nothing to do with
reality.</li><li class="listitem">You can go to production with passing tests and failing production.</li></ul></div><p>To solve the aforementioned issues, Spring Cloud Contract Verifier with Stub Runner was
created. The main idea is to give you very fast feedback, without the need to set up the
whole world of microservices. If you work on stubs, then the only applications you need
are those that your application directly uses.</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/docs/src/main/asciidoc/images/Stubs2.png" alt="Stubbed Services"></div></div><p>Spring Cloud Contract Verifier gives you the certainty that the stubs that you use were
created by the service that you&#8217;re calling. Also, if you can use them, it means that they
were tested against the producer&#8217;s side. In short, you can trust those stubs.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_purposes" href="#_purposes"></a>86.3&nbsp;Purposes</h2></div></div></div><p>The main purposes of Spring Cloud Contract Verifier with Stub Runner are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">To ensure that WireMock/Messaging stubs (used when developing the client) do exactly
what the actual server-side implementation does.</li><li class="listitem">To promote ATDD method and Microservices architectural style.</li><li class="listitem">To provide a way to publish changes in contracts that are immediately visible on both
sides.</li><li class="listitem">To generate boilerplate test code to be used on the server side.</li></ul></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Contract Verifier&#8217;s purpose is NOT to start writing business
features in the contracts. Assume that we have a business use case of fraud check. If a
user can be a fraud for 100 different reasons, we would assume that you would create 2
contracts, one for the positive case and one for the negative case. Contract tests are
used to test contracts between applications and not to simulate full behavior.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_it_works" href="#_how_it_works"></a>86.4&nbsp;How It Works</h2></div></div></div><p>This section explores how Spring Cloud Contract Verifier with Stub Runner works.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-contract-verifier-intro-three-second-tour" href="#spring-cloud-contract-verifier-intro-three-second-tour"></a>86.4.1&nbsp;A Three-second Tour</h3></div></div></div><p>This very brief tour walks through using Spring Cloud Contract:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#spring-cloud-contract-verifier-intro-three-second-tour-producer" title="On the Producer Side">the section called &#8220;On the Producer Side&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-contract-verifier-intro-three-second-tour-consumer" title="On the Consumer Side">the section called &#8220;On the Consumer Side&#8221;</a></li></ul></div><p>You can find a somewhat longer tour
<a class="link" href="#spring-cloud-contract-verifier-intro-three-minute-tour" title="86.4.2&nbsp;A Three-minute Tour">here</a>.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-contract-verifier-intro-three-second-tour-producer" href="#spring-cloud-contract-verifier-intro-three-second-tour-producer"></a>On the Producer Side</h4></div></div></div><p>To start working with Spring Cloud Contract, add files with <code class="literal">REST/</code> messaging contracts
expressed in either Groovy DSL or YAML to the contracts directory, which is set by the
<code class="literal">contractsDslDir</code> property. By default, it is <code class="literal">$rootDir/src/test/resources/contracts</code>.</p><p>Then add the Spring Cloud Contract Verifier dependency and plugin to your build file, as
shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-verifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>The following listing shows how to add the plugin, which should go in the build/plugins
portion of the file:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>Running <code class="literal">./mvnw clean install</code> automatically generates tests that verify the application
compliance with the added contracts. By default, the tests get generated under
<code class="literal">org.springframework.cloud.contract.verifier.tests.</code>.</p><p>As the implementation of the functionalities described by the contracts is not yet
present, the tests fail.</p><p>To make them pass, you must add the correct implementation of either handling HTTP
requests or messages. Also, you must add a correct base test class for auto-generated
tests to the project. This class is extended by all the auto-generated tests, and it
should contain all the setup necessary to run them (for example <code class="literal">RestAssuredMockMvc</code>
controller setup or messaging test setup).</p><p>Once the implementation and the test base class are in place, the tests pass, and both the
application and the stub artifacts are built and installed in the local Maven repository.
The changes can now be merged, and both the application and the stub artifacts may be
published in an online repository.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-contract-verifier-intro-three-second-tour-consumer" href="#spring-cloud-contract-verifier-intro-three-second-tour-consumer"></a>On the Consumer Side</h4></div></div></div><p><code class="literal">Spring Cloud Contract Stub Runner</code> can be used in the integration tests to get a running
WireMock instance or messaging route that simulates the actual service.</p><p>To do so, add the dependency to <code class="literal">Spring Cloud Contract Stub Runner</code>, as shown in the
following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>You can get the Producer-side stubs installed in your Maven repository in either of two
ways:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">By checking out the Producer side repository and adding contracts and generating the stubs
by running the following commands:</p><pre class="programlisting">$ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">cd</span> local-http-server-repo
$ ./mvnw clean install -DskipTests</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>The tests are being skipped because the Producer-side contract implementation is not
in place yet, so the automatically-generated contract tests fail.</p></td></tr></table></div></li><li class="listitem"><p class="simpara">By getting already-existing producer service stubs from a remote repository. To do so,
pass the stub artifact IDs and artifact repository URL as <code class="literal">Spring Cloud Contract
Stub Runner</code> properties, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ids</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example:http-server-dsl:+:stubs:8080'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repositoryRoot</span>: http://repo.spring.io/libs-snapshot</pre></li></ul></div><p>Now you can annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation,
provide the <code class="literal">group-id</code> and <code class="literal">artifact-id</code> values for <code class="literal">Spring Cloud Contract Stub Runner</code> to
run the collaborators' stubs for you, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment=WebEnvironment.NONE)</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceTests {</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Use the <code class="literal">REMOTE</code> <code class="literal">stubsMode</code> when downloading stubs from an online repository and
<code class="literal">LOCAL</code> for offline work.</p></td></tr></table></div><p>Now, in your integration test, you can receive stubbed versions of HTTP responses or
messages that are expected to be emitted by the collaborator service.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="spring-cloud-contract-verifier-intro-three-minute-tour" href="#spring-cloud-contract-verifier-intro-three-minute-tour"></a>86.4.2&nbsp;A Three-minute Tour</h3></div></div></div><p>This brief tour walks through using Spring Cloud Contract:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#spring-cloud-contract-verifier-intro-three-minute-tour-producer" title="On the Producer Side">the section called &#8220;On the Producer Side&#8221;</a></li><li class="listitem"><a class="xref" href="#spring-cloud-contract-verifier-intro-three-minute-tour-consumer" title="On the Consumer Side">the section called &#8220;On the Consumer Side&#8221;</a></li></ul></div><p>You can find an even more brief tour
<a class="link" href="#spring-cloud-contract-verifier-intro-three-second-tour" title="86.4.1&nbsp;A Three-second Tour">here</a>.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-contract-verifier-intro-three-minute-tour-producer" href="#spring-cloud-contract-verifier-intro-three-minute-tour-producer"></a>On the Producer Side</h4></div></div></div><p>To start working with <code class="literal">Spring Cloud Contract</code>, add files with <code class="literal">REST/</code> messaging contracts
expressed in either Groovy DSL or YAML to the contracts directory, which is set by the
<code class="literal">contractsDslDir</code> property. By default, it is <code class="literal">$rootDir/src/test/resources/contracts</code>.</p><p>For the HTTP stubs, a contract defines what kind of response should be returned for a
given request (taking into account the HTTP methods, URLs, headers, status codes, and so
on). The following example shows how an HTTP stub contract in Groovy DSL:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> contracts
org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/fraudcheck'</span>
body([
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"client.id"</span>: $(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{10}'</span>)),
loanAmount: <span class="hl-number">99999</span>
])
headers {
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
response {
status OK()
body([
fraudCheckStatus: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"rejection.reason"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>
])
headers {
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
}</pre><p>The same contract expressed in YAML would look like the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">request</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> method</span>: PUT
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> url</span>: /fraudcheck
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> body</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> "client.id"</span>: <span class="hl-number">1234567890</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> loanAmount</span>: <span class="hl-number">99999</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> headers</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> Content-Type</span>: application/json
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> matchers</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> body</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - path</span>: $.[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'client.id'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> type</span>: by_regex
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> value</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{10}"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">response</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> status</span>: <span class="hl-number">200</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> body</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> fraudCheckStatus</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> "rejection.reason"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> headers</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> Content-Type</span>: application/json;charset=UTF-<span class="hl-number">8</span></pre><p>In the case of messaging, you can define:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The input and the output messages can be defined (taking into account from and where it
was sent, the message body, and the header).</li><li class="listitem">The methods that should be called after the message is received.</li><li class="listitem">The methods that, when called, should trigger a message.</li></ul></div><p>The following example shows a Camel messaging contract expressed in Groovy DSL:</p><pre class="programlisting">def contractDsl = Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:delete'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookWasDeleted()'</span>)
}
}</pre><p>The following example shows the same contract expressed in YAML:</p><pre class="programlisting">label: some_label
input:
messageFrom: jms:delete
messageBody:
bookName: 'foo'
messageHeaders:
sample: header
assertThat: bookWasDeleted()</pre><p>Then you can add Spring Cloud Contract Verifier dependency and plugin to your build file,
as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-verifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>The following listing shows how to add the plugin, which should go in the build/plugins
portion of the file:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>Running <code class="literal">./mvnw clean install</code> automatically generates tests that verify the application
compliance with the added contracts. By default, the generated tests are under
<code class="literal">org.springframework.cloud.contract.verifier.tests.</code>.</p><p>The following example shows a sample auto-generated test for an HTTP contract:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_shouldMarkClientAsFraud() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"client.id\":\"1234567890\",\"loanAmount\":99999}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1.json.*"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['fraudCheckStatus']"</span>).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[A-Z]{5}"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['rejection.reason']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
}</pre><p>The preceding example uses Spring&#8217;s <code class="literal">MockMvc</code> to run the tests. This is the default test
mode for HTTP contracts. However, JAX-RS client and explicit HTTP invocations can also be
used. (To do so, change the <code class="literal">testMode</code> property of the plugin to <code class="literal">JAX-RS</code> or <code class="literal">EXPLICIT</code>,
respectively.)</p><p>Since 2.1.0, it is also possible to use <code class="literal">RestAssuredWebTestClient`with Spring&#8217;s reactive `WebTestClient</code>
run under the hood. This is particularly recommended while working with Reactive, <code class="literal">Web-Flux</code>-based applications.
In order to use <code class="literal">WebTestClient</code> set <code class="literal">testMode</code> to <code class="literal">WEBTESTCLIENT</code>.</p><p>Here is an example of a test generated in <code class="literal">WEBTESTCLIENT</code> test mode:</p><pre class="literallayout">[source,java,indent=0]</pre><pre class="screen">@Test
public void validate_shouldRejectABeerIfTooYoung() throws Exception {
// given:
WebTestClientRequestSpecification request = given()
.header("Content-Type", "application/json")
.body("{\"age\":10}");
// when:
WebTestClientResponse response = given().spec(request)
.post("/check");
// then:
assertThat(response.statusCode()).isEqualTo(200);
assertThat(response.header("Content-Type")).matches("application/json.*");
// and:
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field("['status']").isEqualTo("NOT_OK");
}</pre><p>Apart from the default JUnit 4, you can instead use JUnit 5 or Spock tests, by setting the plugin
<code class="literal">testFramework</code> property to either <code class="literal">JUNIT5</code> or <code class="literal">Spock</code>.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You can now also generate WireMock scenarios based on the contracts, by including an
order number followed by an underscore at the beginning of the contract file names.</p></td></tr></table></div><p>The following example shows an auto-generated test in Spock for a messaging stub contract:</p><pre class="literallayout">[source,groovy,indent=0]</pre><pre class="screen">given:
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
\'\'\'{"bookName":"foo"}\'\'\',
['sample': 'header']
)
when:
contractVerifierMessaging.send(inputMessage, 'jms:delete')
then:
noExceptionThrown()
bookWasDeleted()</pre><p>As the implementation of the functionalities described by the contracts is not yet
present, the tests fail.</p><p>To make them pass, you must add the correct implementation of handling either HTTP
requests or messages. Also, you must add a correct base test class for auto-generated
tests to the project. This class is extended by all the auto-generated tests and should
contain all the setup necessary to run them (for example, <code class="literal">RestAssuredMockMvc</code> controller
setup or messaging test setup).</p><p>Once the implementation and the test base class are in place, the tests pass, and both the
application and the stub artifacts are built and installed in the local Maven repository.
Information about installing the stubs jar to the local repository appears in the logs, as
shown in the following example:</p><pre class="programlisting">[INFO] --- spring-cloud-contract-maven-plugin:<span class="hl-number">1.0</span>.<span class="hl-number">0.</span>BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
[INFO]
[INFO] --- maven-jar-plugin:<span class="hl-number">2.6</span>:jar (default-jar) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:<span class="hl-number">1.5</span>.<span class="hl-number">5.</span>BUILD-SNAPSHOT:repackage (default) @ http-server ---
[INFO]
[INFO] --- maven-install-plugin:<span class="hl-number">2.5</span>.<span class="hl-number">2</span>:install (default-install) @ http-server ---
[INFO] Installing /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar
[INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
[INFO] Installing /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar</pre><p>You can now merge the changes and publish both the application and the stub artifacts
in an online repository.</p><p><span class="strong"><strong>Docker Project</strong></span></p><p>In order to enable working with contracts while creating applications in non-JVM
technologies, the <code class="literal">springcloud/spring-cloud-contract</code> Docker image has been created. It
contains a project that automatically generates tests for HTTP contracts and executes them
in <code class="literal">EXPLICIT</code> test mode. Then, if the tests pass, it generates Wiremock stubs and,
optionally, publishes them to an artifact manager. In order to use the image, you can
mount the contracts into the <code class="literal">/contracts</code> directory and set a few environment variables.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="spring-cloud-contract-verifier-intro-three-minute-tour-consumer" href="#spring-cloud-contract-verifier-intro-three-minute-tour-consumer"></a>On the Consumer Side</h4></div></div></div><p><code class="literal">Spring Cloud Contract Stub Runner</code> can be used in the integration tests to get a running
WireMock instance or messaging route that simulates the actual service.</p><p>To get started, add the dependency to <code class="literal">Spring Cloud Contract Stub Runner</code>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>You can get the Producer-side stubs installed in your Maven repository in either of two
ways:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">By checking out the Producer side repository and adding contracts and generating the
stubs by running the following commands:</p><pre class="programlisting">$ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">cd</span> local-http-server-repo
$ ./mvnw clean install -DskipTests</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The tests are skipped because the Producer-side contract implementation is not yet
in place, so the automatically-generated contract tests fail.</p></td></tr></table></div></li><li class="listitem"><p class="simpara">Getting already existing producer service stubs from a remote repository. To do so,
pass the stub artifact IDs and artifact repository URl as <code class="literal">Spring Cloud Contract Stub
Runner</code> properties, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ids</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example:http-server-dsl:+:stubs:8080'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repositoryRoot</span>: http://repo.spring.io/libs-snapshot</pre></li></ul></div><p>Now you can annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation,
provide the <code class="literal">group-id</code> and <code class="literal">artifact-id</code> for <code class="literal">Spring Cloud Contract Stub Runner</code> to run
the collaborators' stubs for you, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment=WebEnvironment.NONE)</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceTests {</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Use the <code class="literal">REMOTE</code> <code class="literal">stubsMode</code> when downloading stubs from an online repository and
<code class="literal">LOCAL</code> for offline work.</p></td></tr></table></div><p>In your integration test, you can receive stubbed versions of HTTP responses or messages
that are expected to be emitted by the collaborator service. You can see entries similar
to the following in the build logs:</p><pre class="programlisting"><span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.403</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Desired version is + - will try to resolve the latest version
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.438</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved version is <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.439</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolving artifact com.example:http-server:jar:stubs:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT using remote repositories []
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.451</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved artifact com.example:http-server:jar:stubs:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.465</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar]
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.475</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacked file to [/var/folders/<span class="hl-number">0</span>p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">27.737</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.StubRunnerExecutor : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT:stubs=<span class="hl-number">8080</span>}]</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_defining_the_contract" href="#_defining_the_contract"></a>86.4.3&nbsp;Defining the Contract</h3></div></div></div><p>As consumers of services, we need to define what exactly we want to achieve. We need to
formulate our expectations. That is why we write contracts.</p><p>Assume that you want to send a request containing the ID of a client company and the
amount it wants to borrow from us. You also want to send it to the /fraudcheck url via
the PUT method.</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> contracts
org.springframework.cloud.contract.spec.Contract.make {
request { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (1)</span>
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (2)</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/fraudcheck'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (3)</span>
body([ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (4)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"client.id"</span>: $(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{10}'</span>)),
loanAmount: <span class="hl-number">99999</span>
])
headers { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (5)</span>
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
response { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (6)</span>
status OK() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (7)</span>
body([ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (8)</span>
fraudCheckStatus: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"rejection.reason"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>
])
headers { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (9)</span>
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">/*
From the Consumer perspective, when shooting a request in the integration test:
(1) - If the consumer sends a request
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `client.id` that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the response will be sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` equal to `application/json`
From the Producer perspective, in the autogenerated producer-side test:
(1) - A request will be sent to the producer
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `client.id` that will have a generated value that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the test will assert if the response has been sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` matching `application/json.*`
*/</span></pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request: # (1)
method: PUT # (2)
url: /fraudcheck # (3)
body: # (4)
"client.id": 1234567890
loanAmount: 99999
headers: # (5)
Content-Type: application/json
matchers:
body:
- path: $.['client.id'] # (6)
type: by_regex
value: "[0-9]{10}"
response: # (7)
status: 200 # (8)
body: # (9)
fraudCheckStatus: "FRAUD"
"rejection.reason": "Amount too high"
headers: # (10)
Content-Type: application/json;charset=UTF-8
#From the Consumer perspective, when shooting a request in the integration test:
#
#(1) - If the consumer sends a request
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `client.id`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
#(7) - then the response will be sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json`
#
#From the Producer perspective, in the autogenerated producer-side test:
#
#(1) - A request will be sent to the producer
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `client.id` `1234567890`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(7) - then the test will assert if the response has been sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_client_side" href="#_client_side"></a>86.4.4&nbsp;Client Side</h3></div></div></div><p>Spring Cloud Contract generates stubs, which you can use during client-side testing.
You get a running WireMock instance/Messaging route that simulates the service.
You would like to feed that instance with a proper stub definition.</p><p>At some point in time, you need to send a request to the Fraud Detection service.</p><pre class="programlisting">ResponseEntity&lt;FraudServiceResponse&gt; response =
restTemplate.exchange(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:"</span> + port + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>, HttpMethod.PUT,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpEntity&lt;&gt;(request, httpHeaders),
FraudServiceResponse.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);</pre><p>Annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation provide the group id and artifact id for the Stub Runner to download stubs of your collaborators.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment=WebEnvironment.NONE)</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceTests {</pre><p>After that, during the tests, Spring Cloud Contract automatically finds the stubs
(simulating the real service) in the Maven repository and exposes them on a configured
(or random) port.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_server_side" href="#_server_side"></a>86.4.5&nbsp;Server Side</h3></div></div></div><p>Since you are developing your stub, you need to be sure that it actually resembles your
concrete implementation. You cannot have a situation where your stub acts in one way and
your application behaves in a different way, especially in production.</p><p>To ensure that your application behaves the way you define in your stub, tests are
generated from the stub you provide.</p><p>The autogenerated test looks, more or less, like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_shouldMarkClientAsFraud() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"client.id\":\"1234567890\",\"loanAmount\":99999}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1.json.*"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['fraudCheckStatus']"</span>).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[A-Z]{5}"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['rejection.reason']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_step_by_step_guide_to_consumer_driven_contracts_cdc" href="#_step_by_step_guide_to_consumer_driven_contracts_cdc"></a>86.5&nbsp;Step-by-step Guide to Consumer Driven Contracts (CDC)</h2></div></div></div><p>Consider an example of Fraud Detection and the Loan Issuance process. The business
scenario is such that we want to issue loans to people but do not want them to steal from
us. The current implementation of our system grants loans to everybody.</p><p>Assume that <code class="literal">Loan Issuance</code> is a client to the <code class="literal">Fraud Detection</code> server. In the current
sprint, we must develop a new feature: if a client wants to borrow too much money, then
we mark the client as a fraud.</p><p>Technical remark - Fraud Detection has an <code class="literal">artifact-id</code> of <code class="literal">http-server</code>, while Loan
Issuance has an artifact-id of <code class="literal">http-client</code>, and both have a <code class="literal">group-id</code> of <code class="literal">com.example</code>.</p><p>Social remark - both client and server development teams need to communicate directly and
discuss changes while going through the process. CDC is all about communication.</p><p>The <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/dsl/http-server" target="_top">server
side code is available here</a> and <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/dsl/http-client" target="_top">the
client code here</a>.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>In this case, the producer owns the contracts. Physically, all the contract are
in the producer&#8217;s repository.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_technical_note" href="#_technical_note"></a>86.5.1&nbsp;Technical note</h3></div></div></div><p>If using the <span class="strong"><strong>SNAPSHOT</strong></span> / <span class="strong"><strong>Milestone</strong></span> / <span class="strong"><strong>Release Candidate</strong></span> versions please add the
following section to your build:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepositories&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">repositories {
mavenCentral()
mavenLocal()
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/snapshot"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/milestone"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/release"</span> }
}</pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_consumer_side_loan_issuance" href="#_consumer_side_loan_issuance"></a>86.5.2&nbsp;Consumer side (Loan Issuance)</h3></div></div></div><p>As a developer of the Loan Issuance service (a consumer of the Fraud Detection server), you might do the following steps:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Start doing TDD by writing a test for your feature.</li><li class="listitem">Write the missing implementation.</li><li class="listitem">Clone the Fraud Detection service repository locally.</li><li class="listitem">Define the contract locally in the repo of Fraud Detection service.</li><li class="listitem">Add the Spring Cloud Contract Verifier plugin.</li><li class="listitem">Run the integration tests.</li><li class="listitem">File a pull request.</li><li class="listitem">Create an initial implementation.</li><li class="listitem">Take over the pull request.</li><li class="listitem">Write the missing implementation.</li><li class="listitem">Deploy your app.</li><li class="listitem">Work online.</li></ol></div><p><span class="strong"><strong>Start doing TDD by writing a test for your feature.</strong></span></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> shouldBeRejectedDueToAbnormalLoanAmount() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
LoanApplication application = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LoanApplication(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Client(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"1234567890"</span>),
<span class="hl-number">99999</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
LoanApplicationResult loanApplication = service.loanApplication(application);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(loanApplication.getLoanApplicationStatus())
.isEqualTo(LoanApplicationStatus.LOAN_APPLICATION_REJECTED);
assertThat(loanApplication.getRejectionReason()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
}</pre><p>Assume that you have written a test of your new feature. If a loan application for a big
amount is received, the system should reject that loan application with some description.</p><p><span class="strong"><strong>Write the missing implementation.</strong></span></p><p>At some point in time, you need to send a request to the Fraud Detection service. Assume
that you need to send the request containing the ID of the client and the amount the
client wants to borrow. You want to send it to the <code class="literal">/fraudcheck</code> url via the <code class="literal">PUT</code> method.</p><pre class="programlisting">ResponseEntity&lt;FraudServiceResponse&gt; response =
restTemplate.exchange(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:"</span> + port + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>, HttpMethod.PUT,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpEntity&lt;&gt;(request, httpHeaders),
FraudServiceResponse.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);</pre><p>For simplicity, the port of the Fraud Detection service is set to <code class="literal">8080</code>, and the
application runs on <code class="literal">8090</code>.</p><p>If you start the test at this point, it breaks, because no service currently runs on port
<code class="literal">8080</code>.</p><p><span class="strong"><strong>Clone the Fraud Detection service repository locally.</strong></span></p><p>You can start by playing around with the server side contract. To do so, you must first
clone it.</p><pre class="programlisting">$ git clone https://your-git-server.com/server-side.git local-http-server-repo</pre><p><span class="strong"><strong>Define the contract locally in the repo of Fraud Detection service.</strong></span></p><p>As a consumer, you need to define what exactly you want to achieve. You need to formulate
your expectations. To do so, write the following contract:</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Place the contract under <code class="literal">src/test/resources/contracts/fraud</code> folder. The <code class="literal">fraud</code> folder
is important because the producer&#8217;s test base class name references that folder.</p></td></tr></table></div><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> contracts
org.springframework.cloud.contract.spec.Contract.make {
request { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (1)</span>
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (2)</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/fraudcheck'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (3)</span>
body([ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (4)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"client.id"</span>: $(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{10}'</span>)),
loanAmount: <span class="hl-number">99999</span>
])
headers { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (5)</span>
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
response { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (6)</span>
status OK() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (7)</span>
body([ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (8)</span>
fraudCheckStatus: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"rejection.reason"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>
])
headers { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (9)</span>
contentType(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
}
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">/*
From the Consumer perspective, when shooting a request in the integration test:
(1) - If the consumer sends a request
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `client.id` that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the response will be sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` equal to `application/json`
From the Producer perspective, in the autogenerated producer-side test:
(1) - A request will be sent to the producer
(2) - With the "PUT" method
(3) - to the URL "/fraudcheck"
(4) - with the JSON body that
* has a field `client.id` that will have a generated value that matches a regular expression `[0-9]{10}`
* has a field `loanAmount` that is equal to `99999`
(5) - with header `Content-Type` equal to `application/json`
(6) - then the test will assert if the response has been sent with
(7) - status equal `200`
(8) - and JSON body equal to
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
(9) - with header `Content-Type` matching `application/json.*`
*/</span></pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request: # (1)
method: PUT # (2)
url: /fraudcheck # (3)
body: # (4)
"client.id": 1234567890
loanAmount: 99999
headers: # (5)
Content-Type: application/json
matchers:
body:
- path: $.['client.id'] # (6)
type: by_regex
value: "[0-9]{10}"
response: # (7)
status: 200 # (8)
body: # (9)
fraudCheckStatus: "FRAUD"
"rejection.reason": "Amount too high"
headers: # (10)
Content-Type: application/json;charset=UTF-8
#From the Consumer perspective, when shooting a request in the integration test:
#
#(1) - If the consumer sends a request
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `client.id`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(6) - and a `client.id` json entry matches the regular expression `[0-9]{10}`
#(7) - then the response will be sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json`
#
#From the Producer perspective, in the autogenerated producer-side test:
#
#(1) - A request will be sent to the producer
#(2) - With the "PUT" method
#(3) - to the URL "/fraudcheck"
#(4) - with the JSON body that
# * has a field `client.id` `1234567890`
# * has a field `loanAmount` that is equal to `99999`
#(5) - with header `Content-Type` equal to `application/json`
#(7) - then the test will assert if the response has been sent with
#(8) - status equal `200`
#(9) - and JSON body equal to
# { "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
#(10) - with header `Content-Type` equal to `application/json;charset=UTF-8`</pre><p>
</p><p>The YML contract is quite straight-forward. However when you take a look at the Contract
written using a statically typed Groovy DSL - you might wonder what the
<code class="literal">value(client(&#8230;&#8203;), server(&#8230;&#8203;))</code> parts are. By using this notation, Spring Cloud
Contract lets you define parts of a JSON block, a URL, etc., which are dynamic. In case
of an identifier or a timestamp, you need not hardcode a value. You want to allow some
different ranges of values. To enable ranges of values, you can set regular expressions
matching those values for the consumer side. You can provide the body by means of either
a map notation or String with interpolations.
Consult the <a class="xref" href="#contract-dsl" title="92.&nbsp;Contract DSL">Chapter&nbsp;92, <i>Contract DSL</i></a> section for more information. We highly recommend using the map notation!</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You must understand the map notation in order to set up contracts. Please read the
<a class="link" href="http://groovy-lang.org/json.html" target="_top">Groovy docs regarding JSON</a>.</p></td></tr></table></div><p>The previously shown contract is an agreement between two sides that:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">if an HTTP request is sent with all of</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">a <code class="literal">PUT</code> method on the <code class="literal">/fraudcheck</code> endpoint,</li><li class="listitem">a JSON body with a <code class="literal">client.id</code> that matches the regular expression <code class="literal">[0-9]{10}</code> and
<code class="literal">loanAmount</code> equal to <code class="literal">99999</code>,</li><li class="listitem">and a <code class="literal">Content-Type</code> header with a value of <code class="literal">application/vnd.fraud.v1+json</code>,</li></ul></div></li><li class="listitem"><p class="simpara">then an HTTP response is sent to the consumer that</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">has status <code class="literal">200</code>,</li><li class="listitem">contains a JSON body with the <code class="literal">fraudCheckStatus</code> field containing a value <code class="literal">FRAUD</code> and
the <code class="literal">rejectionReason</code> field having value <code class="literal">Amount too high</code>,</li><li class="listitem">and a <code class="literal">Content-Type</code> header with a value of <code class="literal">application/vnd.fraud.v1+json</code>.</li></ul></div></li></ul></div><p>Once you are ready to check the API in practice in the integration tests, you need to
install the stubs locally.</p><p><span class="strong"><strong>Add the Spring Cloud Contract Verifier plugin.</strong></span></p><p>We can add either a Maven or a Gradle plugin. In this example, you see how to add Maven.
First, add the <code class="literal">Spring Cloud Contract</code> BOM.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-release.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span></pre><p>Next, add the <code class="literal">Spring Cloud Contract Verifier</code> Maven plugin</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example.fraud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>Since the plugin was added, you get the <code class="literal">Spring Cloud Contract Verifier</code> features which,
from the provided contracts:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">generate and run tests</li><li class="listitem">produce and install stubs</li></ul></div><p>You do not want to generate tests since you, as the consumer, want only to play with the
stubs. You need to skip the test generation and execution. When you execute:</p><pre class="programlisting">$ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">cd</span> local-http-server-repo
$ ./mvnw clean install -DskipTests</pre><p>In the logs, you see something like this:</p><pre class="programlisting">[INFO] --- spring-cloud-contract-maven-plugin:<span class="hl-number">1.0</span>.<span class="hl-number">0.</span>BUILD-SNAPSHOT:generateStubs (default-generateStubs) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
[INFO]
[INFO] --- maven-jar-plugin:<span class="hl-number">2.6</span>:jar (default-jar) @ http-server ---
[INFO] Building jar: /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar
[INFO]
[INFO] --- spring-boot-maven-plugin:<span class="hl-number">1.5</span>.<span class="hl-number">5.</span>BUILD-SNAPSHOT:repackage (default) @ http-server ---
[INFO]
[INFO] --- maven-install-plugin:<span class="hl-number">2.5</span>.<span class="hl-number">2</span>:install (default-install) @ http-server ---
[INFO] Installing /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.jar
[INFO] Installing /some/path/http-server/pom.xml to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
[INFO] Installing /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar</pre><p>The following line is extremely important:</p><pre class="programlisting">[INFO] Installing /some/path/http-server/target/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar</pre><p>It confirms that the stubs of the <code class="literal">http-server</code> have been installed in the local
repository.</p><p><span class="strong"><strong>Run the integration tests.</strong></span></p><p>In order to profit from the Spring Cloud Contract Stub Runner functionality of automatic
stub downloading, you must do the following in your consumer side project (<code class="literal">Loan
Application service</code>):</p><p>Add the <code class="literal">Spring Cloud Contract</code> BOM:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-release-train.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span></pre><p>Add the dependency to <code class="literal">Spring Cloud Contract Stub Runner</code>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>Annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>. In the annotation, provide the
<code class="literal">group-id</code> and <code class="literal">artifact-id</code> for the Stub Runner to download the stubs of your
collaborators. (Optional step) Because you&#8217;re playing with the collaborators offline, you
can also provide the offline work switch (<code class="literal">StubRunnerProperties.StubsMode.LOCAL</code>).</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment=WebEnvironment.NONE)</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = {"com.example:http-server-dsl:+:stubs:6565"},
stubsMode = StubRunnerProperties.StubsMode.LOCAL)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceTests {</pre><p>Now, when you run your tests, you see something like this:</p><pre class="programlisting"><span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.403</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Desired version is + - will try to resolve the latest version
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.438</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved version is <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.439</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolving artifact com.example:http-server:jar:stubs:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT using remote repositories []
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.451</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Resolved artifact com.example:http-server:jar:stubs:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT to /path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.465</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacking stub from JAR [URI: file:/path/to/your/.m2/repository/com/example/http-server/<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT/http-server-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar]
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">25.475</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.AetherStubDownloader : Unpacked file to [/var/folders/<span class="hl-number">0</span>p/xwq47sq106x1_g3dtv6qfm940000gq/T/contracts100276532569594265]
<span class="hl-number">2016</span>-<span class="hl-number">07</span>-<span class="hl-number">19</span> <span class="hl-number">14</span>:<span class="hl-number">22</span>:<span class="hl-number">27.737</span> INFO <span class="hl-number">41050</span> --- [ main] o.s.c.c.stubrunner.StubRunnerExecutor : All stubs are now running RunningStubs [namesAndPorts={com.example:http-server:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT:stubs=<span class="hl-number">8080</span>}]</pre><p>This output means that Stub Runner has found your stubs and started a server for your app
with group id <code class="literal">com.example</code>, artifact id <code class="literal">http-server</code> with version <code class="literal">0.0.1-SNAPSHOT</code> of
the stubs and with <code class="literal">stubs</code> classifier on port <code class="literal">8080</code>.</p><p><span class="strong"><strong>File a pull request.</strong></span></p><p>What you have done until now is an iterative process. You can play around with the
contract, install it locally, and work on the consumer side until the contract works as
you wish.</p><p>Once you are satisfied with the results and the test passes, publish a pull request to
the server side. Currently, the consumer side work is done.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_producer_side_fraud_detection_server" href="#_producer_side_fraud_detection_server"></a>86.5.3&nbsp;Producer side (Fraud Detection server)</h3></div></div></div><p>As a developer of the Fraud Detection server (a server to the Loan Issuance service):</p><p><span class="strong"><strong>Create an initial implementation.</strong></span></p><p>As a reminder, you can see the initial implementation here:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RequestMapping(value = "/fraudcheck", method = PUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> FraudCheckResult fraudCheck(<em><span class="hl-annotation" style="color: gray">@RequestBody</span></em> FraudCheck fraudCheck) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
}</pre><p><span class="strong"><strong>Take over the pull request.</strong></span></p><pre class="programlisting">$ git checkout -b contract-change-pr master
$ git pull https://your-git-server.com/server-side-fork.git contract-change-pr</pre><p>You must add the dependencies needed by the autogenerated tests:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-verifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>In the configuration of the Maven plugin, pass the <code class="literal">packageWithBaseClasses</code> property</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example.fraud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This example uses "convention based" naming by setting the
<code class="literal">packageWithBaseClasses</code> property. Doing so means that the two last packages combine to
make the name of the base test class. In our case, the contracts were placed under
<code class="literal">src/test/resources/contracts/fraud</code>. Since you do not have two packages starting from
the <code class="literal">contracts</code> folder, pick only one, which should be <code class="literal">fraud</code>. Add the <code class="literal">Base</code> suffix and
capitalize <code class="literal">fraud</code>. That gives you the <code class="literal">FraudBase</code> test class name.</p></td></tr></table></div><p>All the generated tests extend that class. Over there, you can set up your Spring Context
or whatever is necessary. In this case, use <a class="link" href="http://rest-assured.io/" target="_top">Rest Assured MVC</a> to
start the server side <code class="literal">FraudDetectionController</code>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example.fraud;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Before;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> io.restassured.module.mockmvc.RestAssuredMockMvc;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FraudBase {
<em><span class="hl-annotation" style="color: gray">@Before</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudDetectionController(),
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudStatsController(stubbedStatsProvider()));
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> StatsProvider stubbedStatsProvider() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> fraudType -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">switch</span> (fraudType) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">case</span> DRUNKS:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">100</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">case</span> ALL:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">200</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span class="hl-number">0</span>;
};
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> assertThatRejectionReasonIsNull(Object rejectionReason) {
assert rejectionReason == null;
}
}</pre><p>Now, if you run the <code class="literal">./mvnw clean install</code>, you get something like this:</p><pre class="programlisting">Results :
Tests in error:
ContractVerifierTest.validate_shouldMarkClientAsFraud:<span class="hl-number">32</span> &raquo; IllegalState Parsed...</pre><p>This error occurs because you have a new contract from which a test was generated and it
failed since you have not implemented the feature. The auto-generated test would look
like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_shouldMarkClientAsFraud() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"client.id\":\"1234567890\",\"loanAmount\":99999}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1.json.*"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['fraudCheckStatus']"</span>).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[A-Z]{5}"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['rejection.reason']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
}</pre><p>If you used the Groovy DSL, you can see, all the <code class="literal">producer()</code> parts of the Contract that were present in the
<code class="literal">value(consumer(&#8230;&#8203;), producer(&#8230;&#8203;))</code> blocks got injected into the test.
In case of using YAML, the same applied for the <code class="literal">matchers</code> sections of the <code class="literal">response</code>.</p><p>Note that, on the producer side, you are also doing TDD. The expectations are expressed
in the form of a test. This test sends a request to our own application with the URL,
headers, and body defined in the contract. It also is expecting precisely defined values
in the response. In other words, you have the <code class="literal">red</code> part of <code class="literal">red</code>, <code class="literal">green</code>, and
<code class="literal">refactor</code>. It is time to convert the <code class="literal">red</code> into the <code class="literal">green</code>.</p><p><span class="strong"><strong>Write the missing implementation.</strong></span></p><p>Because you know the expected input and expected output, you can write the missing
implementation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RequestMapping(value = "/fraudcheck", method = PUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> FraudCheckResult fraudCheck(<em><span class="hl-annotation" style="color: gray">@RequestBody</span></em> FraudCheck fraudCheck) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (amountGreaterThanThreshold(fraudCheck)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudCheckResult(FraudCheckStatus.FRAUD, AMOUNT_TOO_HIGH);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudCheckResult(FraudCheckStatus.OK, NO_REASON);
}</pre><p>When you execute <code class="literal">./mvnw clean install</code> again, the tests pass. Since the <code class="literal">Spring Cloud
Contract Verifier</code> plugin adds the tests to the <code class="literal">generated-test-sources</code>, you can
actually run those tests from your IDE.</p><p><span class="strong"><strong>Deploy your app.</strong></span></p><p>Once you finish your work, you can deploy your change. First, merge the branch:</p><pre class="programlisting">$ git checkout master
$ git merge --no-ff contract-change-pr
$ git push origin master</pre><p>Your CI might run something like <code class="literal">./mvnw clean deploy</code>, which would publish both the
application and the stub artifacts.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_consumer_side_loan_issuance_final_step" href="#_consumer_side_loan_issuance_final_step"></a>86.5.4&nbsp;Consumer Side (Loan Issuance) Final Step</h3></div></div></div><p>As a developer of the Loan Issuance service (a consumer of the Fraud Detection server):</p><p><span class="strong"><strong>Merge branch to master.</strong></span></p><pre class="programlisting">$ git checkout master
$ git merge --no-ff contract-change-pr</pre><p><span class="strong"><strong>Work online.</strong></span></p><p>Now you can disable the offline work for Spring Cloud Contract Stub Runner and indicate
where the repository with your stubs is located. At this moment the stubs of the server
side are automatically downloaded from Nexus/Artifactory. You can set the value of
<code class="literal">stubsMode</code> to <code class="literal">REMOTE</code>. The following code shows an example of
achieving the same thing by changing the properties.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ids</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example:http-server-dsl:+:stubs:8080'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repositoryRoot</span>: http://repo.spring.io/libs-snapshot</pre><p>That&#8217;s it!</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_dependencies" href="#_dependencies"></a>86.6&nbsp;Dependencies</h2></div></div></div><p>The best way to add dependencies is to use the proper <code class="literal">starter</code> dependency.</p><p>For <code class="literal">stub-runner</code>, use <code class="literal">spring-cloud-starter-stub-runner</code>. When you use a plugin, add
<code class="literal">spring-cloud-starter-contract-verifier</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_additional_links" href="#_additional_links"></a>86.7&nbsp;Additional Links</h2></div></div></div><p>Here are some resources related to Spring Cloud Contract Verifier and Stub Runner. Note
that some may be outdated, because the Spring Cloud Contract Verifier project is under
constant development.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_spring_cloud_contract_video" href="#_spring_cloud_contract_video"></a>86.7.1&nbsp;Spring Cloud Contract video</h3></div></div></div><p>You can check out the video from the Warsaw JUG about Spring Cloud Contract:</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_readings" href="#_readings"></a>86.7.2&nbsp;Readings</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="http://www.slideshare.net/MarcinGrzejszczak/stick-to-the-rules-consumer-driven-contracts-201507-confitura" target="_top">Slides from Marcin Grzejszczak&#8217;s talk about Accurest</a></li><li class="listitem"><a class="link" href="http://toomuchcoding.com/blog/categories/accurest/" target="_top">Accurest related articles from Marcin Grzejszczak&#8217;s blog</a></li><li class="listitem"><a class="link" href="http://toomuchcoding.com/blog/categories/spring-cloud-contract/" target="_top">Spring Cloud Contract related articles from Marcin Grzejszczak&#8217;s blog</a></li><li class="listitem"><a class="link" href="http://groovy-lang.org/json.html" target="_top">Groovy docs regarding JSON</a></li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_samples_2" href="#_samples_2"></a>86.8&nbsp;Samples</h2></div></div></div><p>You can find some samples at
<a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples" target="_top">samples</a>.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_contract_faq" href="#_spring_cloud_contract_faq"></a>87.&nbsp;Spring Cloud Contract FAQ</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_why_use_spring_cloud_contract_verifier_and_not_x" href="#_why_use_spring_cloud_contract_verifier_and_not_x"></a>87.1&nbsp;Why use Spring Cloud Contract Verifier and not X ?</h2></div></div></div><p>For the time being Spring Cloud Contract is a JVM based tool. So it could be your first pick when you&#8217;re already creating
software for the JVM. This project has a lot of really interesting features but especially quite a few of them definitely make
Spring Cloud Contract Verifier stand out on the "market" of Consumer Driven Contract (CDC) tooling. Out of many the most interesting are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Possibility to do CDC with messaging</li><li class="listitem">Clear and easy to use, statically typed DSL</li><li class="listitem">Possibility to copy paste your current JSON file to the contract and only edit its elements</li><li class="listitem">Automatic generation of tests from the defined Contract</li><li class="listitem">Stub Runner functionality - the stubs are automatically downloaded at runtime from Nexus / Artifactory</li><li class="listitem">Spring Cloud integration - no discovery service is needed for integration tests</li><li class="listitem">Spring Cloud Contract integrates with Pact out of the box and provides easy hooks to extend its functionality</li><li class="listitem">Via Docker adds support for any language &amp; framework used</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_i_don_t_want_to_write_a_contract_in_groovy" href="#_i_don_t_want_to_write_a_contract_in_groovy"></a>87.2&nbsp;I don&#8217;t want to write a contract in Groovy!</h2></div></div></div><p>No problem. You can write a contract in YAML!</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_what_is_this_value_consumer_producer" href="#_what_is_this_value_consumer_producer"></a>87.3&nbsp;What is this value(consumer(), producer()) ?</h2></div></div></div><p>One of the biggest challenges related to stubs is their reusability. Only if they can be vastly used, will they serve their purpose.
What typically makes that difficult are the hard-coded values of request / response elements. For example dates or ids.
Imagine the following JSON request</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2016-10-10 20:10:15"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"9febab1c-6f36-4a0b-88d6-3b6a6d81cd4a"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>and JSON response</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2016-10-10 21:10:15"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"c4231e1f-3ca9-48d3-b7e7-567d55f0d051"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>Imagine the pain required to set proper value of the <code class="literal">time</code> field (let&#8217;s assume that this content is generated by the
database) by changing the clock in the system or providing stub implementations of data providers. The same is related
to the field called <code class="literal">id</code>. Will you create a stubbed implementation of UUID generator? Makes little sense&#8230;&#8203;</p><p>So as a consumer you would like to send a request that matches any form of a time or any UUID. That way your system
will work as usual - will generate data and you won&#8217;t have to stub anything out. Let&#8217;s assume that in case of the aforementioned
JSON the most important part is the <code class="literal">body</code> field. You can focus on that and provide matching for other fields. In other words
you would like the stub to work like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"SOMETHING THAT MATCHES TIME"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"SOMETHING THAT MATCHES UUID"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>As far as the response goes as a consumer you need a concrete value that you can operate on. So such a JSON is valid</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2016-10-10 21:10:15"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"c4231e1f-3ca9-48d3-b7e7-567d55f0d051"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>As you could see in the previous sections we generate tests from contracts. So from the producer&#8217;s side the situation looks
much different. We&#8217;re parsing the provided contract and in the test we want to send a real request to your endpoints.
So for the case of a producer for the request we can&#8217;t have any sort of matching. We need concrete values that the
producer&#8217;s backend can work on. Such a JSON would be a valid one:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"2016-10-10 20:10:15"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"9febab1c-6f36-4a0b-88d6-3b6a6d81cd4a"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>On the other hand from the point of view of the validity of the contract the response doesn&#8217;t necessarily have to
contain concrete values of <code class="literal">time</code> or <code class="literal">id</code>. Let&#8217;s say that you generate those on the producer side - again, you&#8217;d
have to do a lot of stubbing to ensure that you always return the same values. That&#8217;s why from the producer&#8217;s side
what you might want is the following response:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"time"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"SOMETHING THAT MATCHES TIME"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"SOMETHING THAT MATCHES UUID"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>How can you then provide one time a matcher for the consumer and a concrete value for the producer and vice versa?
In Spring Cloud Contract we&#8217;re allowing you to provide a <span class="strong"><strong>dynamic value</strong></span>. That means that it can differ for both
sides of the communication. You can pass the values:</p><p>Either via the <code class="literal">value</code> method</p><pre class="programlisting">value(consumer(...), producer(...))
value(stub(...), test(...))
value(client(...), server(...))</pre><p>or using the <code class="literal">$()</code> method</p><pre class="programlisting">$(consumer(...), producer(...))
$(stub(...), test(...))
$(client(...), server(...))</pre><p>You can read more about this in the <a class="xref" href="#contract-dsl" title="92.&nbsp;Contract DSL">Chapter&nbsp;92, <i>Contract DSL</i></a> section.</p><p>Calling <code class="literal">value()</code> or <code class="literal">$()</code> tells Spring Cloud Contract that you will be passing a dynamic value.
Inside the <code class="literal">consumer()</code> method you pass the value that should be used on the consumer side (in the generated stub).
Inside the <code class="literal">producer()</code> method you pass the value that should be used on the producer side (in the generated test).</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>If on one side you have passed the regular expression and you haven&#8217;t passed the other, then the
other side will get auto-generated.</p></td></tr></table></div><p>Most often you will use that method together with the <code class="literal">regex</code> helper method. E.g. <code class="literal">consumer(regex('[0-9]{10}'))</code>.</p><p>To sum it up the contract for the aforementioned scenario would look more or less like this (the regular expression
for time and UUID are simplified and most likely invalid but we want to keep things very simple in this example):</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/someUrl'</span>
body([
time : value(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]-[0-5][0-9]-[0-5][0-9]'</span>)),
id: value(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9a-zA-z]{8}-[0-9a-zA-z]{4}-[0-9a-zA-z]{4}-[0-9a-zA-z]{12}'</span>))
body: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
])
}
response {
status OK()
body([
time : value(producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9]{4}-[0-9]{2}-[0-9]{2} [0-2][0-9]-[0-5][0-9]-[0-5][0-9]'</span>)),
id: value([producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[0-9a-zA-z]{8}-[0-9a-zA-z]{4}-[0-9a-zA-z]{4}-[0-9a-zA-z]{12}'</span>))
body: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
])
}
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Please read the <a class="link" href="http://groovy-lang.org/json.html" target="_top">Groovy docs related to JSON</a> to understand how to
properly structure the request / response bodies.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_to_do_stubs_versioning" href="#_how_to_do_stubs_versioning"></a>87.4&nbsp;How to do Stubs versioning?</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_api_versioning" href="#_api_versioning"></a>87.4.1&nbsp;API Versioning</h3></div></div></div><p>Let&#8217;s try to answer a question what versioning really means. If you&#8217;re referring to the API version then there are
different approaches.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">use Hypermedia, links and do not version your API by any means</li><li class="listitem">pass versions through headers / urls</li></ul></div><p>I will not try to answer a question which approach is better. Whatever suits your needs and allows you to generate
business value should be picked.</p><p>Let&#8217;s assume that you do version your API. In that case you should provide as many contracts as many versions you support.
You can create a subfolder for every version or append it to the contract name - whatever suits you more.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_jar_versioning" href="#_jar_versioning"></a>87.4.2&nbsp;JAR versioning</h3></div></div></div><p>If by versioning you mean the version of the JAR that contains the stubs then there are essentially two main approaches.</p><p>Let&#8217;s assume that you&#8217;re doing Continuous Delivery / Deployment which means that you&#8217;re generating a new version of
the jar each time you go through the pipeline and that jar can go to production at any time. For example your jar version
looks like this (it got built on the 20.10.2016 at 20:15:21) :</p><pre class="programlisting"><span class="hl-number">1.0</span>.<span class="hl-number">0.20161020</span>-<span class="hl-number">201521</span>-RELEASE</pre><p>In that case your generated stub jar will look like this.</p><pre class="programlisting"><span class="hl-number">1.0</span>.<span class="hl-number">0.20161020</span>-<span class="hl-number">201521</span>-RELEASE-stubs.jar</pre><p>In this case you should inside your <code class="literal">application.yml</code> or <code class="literal">@AutoConfigureStubRunner</code> when referencing stubs provide the
latest version of the stubs. You can do that by passing the <code class="literal">+</code> sign. Example</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:http-server-dsl:+:stubs:8080"</span>})</pre><p>If the versioning however is fixed (e.g. <code class="literal">1.0.4.RELEASE</code> or <code class="literal">2.1.1</code>) then you have to set the concrete value of the jar
version. Example for 2.1.1.</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:http-server-dsl:2.1.1:stubs:8080"</span>})</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_dev_or_prod_stubs" href="#_dev_or_prod_stubs"></a>87.4.3&nbsp;Dev or prod stubs</h3></div></div></div><p>You can manipulate the classifier to run the tests against current development version of the stubs of other services
or the ones that were deployed to production. If you alter your build to deploy the stubs with the <code class="literal">prod-stubs</code> classifier
once you reach production deployment then you can run tests in one case with dev stubs and one with prod stubs.</p><p>Example of tests using development version of stubs</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:http-server-dsl:+:stubs:8080"</span>})</pre><p>Example of tests using production version of stubs</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:http-server-dsl:+:prod-stubs:8080"</span>})</pre><p>You can pass those values also via properties from your deployment pipeline.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_common_repo_with_contracts" href="#_common_repo_with_contracts"></a>87.5&nbsp;Common repo with contracts</h2></div></div></div><p>Another way of storing contracts other than having them with the producer is keeping them in a common place.
It can be related to security issues where the consumers can&#8217;t clone the producer&#8217;s code. Also if you keep
contracts in a single place then you, as a producer, will know how many consumers you have and which
consumer you will break with your local changes.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_repo_structure" href="#_repo_structure"></a>87.5.1&nbsp;Repo structure</h3></div></div></div><p>Let&#8217;s assume that we have a producer with coordinates <code class="literal">com.example:server</code> and 3 consumers: <code class="literal">client1</code>,
<code class="literal">client2</code>, <code class="literal">client3</code>. Then in the repository with common contracts you would have the following setup
(which you can checkout <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples/standalone/contracts" target="_top">here</a>):</p><pre class="programlisting">&#9500;&#9472;&#9472; com
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; example
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; server
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; client1
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; expectation.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; client2
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; expectation.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; client3
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; expectation.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; pom.xml
&#9500;&#9472;&#9472; mvnw
&#9500;&#9472;&#9472; mvnw.cmd
&#9500;&#9472;&#9472; pom.xml
&#9492;&#9472;&#9472; src
&#9492;&#9472;&#9472; assembly
&#9492;&#9472;&#9472; contracts.xml</pre><p>As you can see under the slash-delimited groupid <code class="literal">/</code> artifact id folder (<code class="literal">com/example/server</code>) you have
expectations of the 3 consumers (<code class="literal">client1</code>, <code class="literal">client2</code> and <code class="literal">client3</code>). Expectations are the standard Groovy DSL
contract files as described throughout this documentation. This repository has to produce a JAR file that maps
one to one to the contents of the repo.</p><p>Example of a <code class="literal">pom.xml</code> inside the <code class="literal">server</code> folder.</p><pre class="programlisting"><span class="hl-directive" style="color: maroon">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;project</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/POM/4.0.0"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;modelVersion&gt;</span>4.0.0<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/modelVersion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>server<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>0.0.1-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Server Stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;description&gt;</span>POM used to install locally stubs for consumer side<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/description&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-parent<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>2.1.0.RELEASE<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;relativePath /&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;project.build.sourceEncoding&gt;</span>UTF-8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/project.build.sourceEncoding&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;java.version&gt;</span>1.8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/java.version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;spring-cloud-contract.version&gt;</span>2.1.0.BUILD-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/spring-cloud-contract.version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;spring-cloud-release.version&gt;</span>Greenwich.BUILD-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/spring-cloud-release.version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;excludeBuildFolders&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/excludeBuildFolders&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-release.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- By default it would search under src/test/resources/ --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsDirectory&gt;</span>${project.basedir}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/project&gt;</span></pre><p>As you can see there are no dependencies other than the Spring Cloud Contract Maven Plugin.
Those poms are necessary for the consumer side to run <code class="literal">mvn clean install -DskipTests</code> to locally install
stubs of the producer project.</p><p>The <code class="literal">pom.xml</code> in the root folder can look like this:</p><pre class="programlisting"><span class="hl-directive" style="color: maroon">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;project</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/POM/4.0.0"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;modelVersion&gt;</span>4.0.0<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/modelVersion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example.standalone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>0.0.1-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;description&gt;</span>Contains all the Spring Cloud Contracts, well, contracts. JAR used by the producers to generate tests and stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/description&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;project.build.sourceEncoding&gt;</span>UTF-8<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/project.build.sourceEncoding&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.apache.maven.plugins<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>maven-assembly-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;phase&gt;</span>prepare-package<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/phase&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>single<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;attach&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/attach&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;descriptor&gt;</span>${basedir}/src/assembly/contracts.xml<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/descriptor&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- If you want an explicit classifier remove the following line --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appendAssemblyId&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/appendAssemblyId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/project&gt;</span></pre><p>It&#8217;s using the assembly plugin in order to build the JAR with all the contracts. Example of such setup is here:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;assembly</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>project<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;format&gt;</span>jar<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/format&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includeBaseDirectory&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includeBaseDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.basedir}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;useDefaultExcludes&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/useDefaultExcludes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;excludes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>**/${project.build.directory}/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>mvnw<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>mvnw.cmd<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>.mvn/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclude&gt;</span>src/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclude&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/excludes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/assembly&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_workflow" href="#_workflow"></a>87.5.2&nbsp;Workflow</h3></div></div></div><p>The workflow would look similar to the one presented in the <code class="literal">Step by step guide to CDC</code>. The only difference
is that the producer doesn&#8217;t own the contracts anymore. So the consumer and the producer have to work on
common contracts in a common repository.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_consumer" href="#_consumer"></a>87.5.3&nbsp;Consumer</h3></div></div></div><p>When the <span class="strong"><strong>consumer</strong></span> wants to work on the contracts offline, instead of cloning the producer code, the
consumer team clones the common repository, goes to the required producer&#8217;s folder (e.g. <code class="literal">com/example/server</code>)
and runs <code class="literal">mvn clean install -DskipTests</code> to install locally the stubs converted from the contracts.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You need to have <a class="link" href="http://maven.apache.org/download.cgi" target="_top">Maven installed locally</a></p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_producer" href="#_producer"></a>87.5.4&nbsp;Producer</h3></div></div></div><p>As a <span class="strong"><strong>producer</strong></span> it&#8217;s enough to alter the Spring Cloud Contract Verifier to provide the URL and the dependency
of the JAR containing the contracts:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsMode&gt;</span>REMOTE<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsMode&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsRepositoryUrl&gt;</span>http://link/to/your/nexus/or/artifactory/or/sth<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsRepositoryUrl&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example.standalone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>With this setup the JAR with groupid <code class="literal">com.example.standalone</code> and artifactid <code class="literal">contracts</code> will be downloaded
from <code class="literal"><a class="link" href="http://link/to/your/nexus/or/artifactory/or/sth" target="_top">http://link/to/your/nexus/or/artifactory/or/sth</a></code>. It will be then unpacked in a local temporary folder
and contracts present under the <code class="literal">com/example/server</code> will be picked as the ones used to generate the
tests and the stubs. Due to this convention the producer team will know which consumer teams will be broken
when some incompatible changes are done.</p><p>The rest of the flow looks the same.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_can_i_define_messaging_contracts_per_topic_not_per_producer" href="#_how_can_i_define_messaging_contracts_per_topic_not_per_producer"></a>87.5.5&nbsp;How can I define messaging contracts per topic not per producer?</h3></div></div></div><p>To avoid messaging contracts duplication in the common repo, when few producers writing messages to one topic,
we could create the structure when the rest contracts would be placed in a folder per producer and messaging
contracts in the folder per topic.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_for_maven_project" href="#_for_maven_project"></a>For Maven Project</h4></div></div></div><p>To make it possible to work on the producer side we should specify an inclusion pattern for
filtering common repository jar by messaging topics we are interested in. <code class="literal"><code class="literal">includedFiles</code></code> property of <code class="literal"><code class="literal">Maven Spring Cloud Contract plugin</code></code>
allows us to do that. Also <code class="literal"><code class="literal">contractsPath</code></code> need to be specified since the default path would be the common repository <code class="literal"><code class="literal">groupid/artifactid</code></code>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsMode&gt;</span>REMOTE<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsMode&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsRepositoryUrl&gt;</span>http://link/to/your/nexus/or/artifactory/or/sth<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsRepositoryUrl&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>common-repo-with-contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>+<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsPath&gt;</span>/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsPath&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractPackageRegex&gt;</span>.*messaging.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractPackageRegex&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassFQN&gt;</span>com.example.services.MessagingBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassFQN&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractPackageRegex&gt;</span>.*rest.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractPackageRegex&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassFQN&gt;</span>com.example.services.TestBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassFQN&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includedFiles&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includedFile&gt;</span>**/${project.artifactId}/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includedFile&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includedFile&gt;</span>**/${first-topic}/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includedFile&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includedFile&gt;</span>**/${second-topic}/**<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includedFile&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includedFiles&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_for_gradle_project" href="#_for_gradle_project"></a>For Gradle Project</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Add a custom configuration for the common-repo dependency:</li></ul></div><pre class="programlisting">ext {
conractsGroupId = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example"</span>
contractsArtifactId = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"common-repo"</span>
contractsVersion = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"1.2.3"</span>
}
configurations {
contracts {
transitive = false
}
}</pre><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Add the common-repo dependency to your classpath:</li></ul></div><pre class="programlisting">dependencies {
contracts <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${conractsGroupId}:${contractsArtifactId}:${contractsVersion}"</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${conractsGroupId}:${contractsArtifactId}:${contractsVersion}"</span>
}</pre><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Download the dependency to an appropriate folder:</li></ul></div><pre class="programlisting">task getContracts(type: Copy) {
from configurations.contracts
into <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> File(project.buildDir, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"downloadedContracts"</span>)
}</pre><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Unzip JAR:</li></ul></div><pre class="programlisting">task unzipContracts(type: Copy) {
def zipFile = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> File(project.buildDir, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"downloadedContracts/${contractsArtifactId}-${contractsVersion}.jar"</span>)
def outputDir = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${buildDir}/unpackedContracts"</span>)
from zipTree(zipFile)
into outputDir
}</pre><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Cleanup unused contracts:</li></ul></div><pre class="programlisting">task deleteUnwantedContracts(type: Delete) {
delete fileTree(dir: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${buildDir}/unpackedContracts"</span>,
include: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"**/*"</span>,
excludes: [
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"**/${project.name}/**"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",
</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"**/${first-topic}/**"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"**/${second-topic}/**"</span>])
}</pre><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Create task dependencies:</li></ul></div><pre class="programlisting">unzipContracts.dependsOn(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"getContracts"</span>)
deleteUnwantedContracts.dependsOn(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"unzipContracts"</span>)
build.dependsOn(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"deleteUnwantedContracts"</span>)</pre><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Configure plugin by specifying the directory containing contracts using <code class="literal">contractsDslDir</code> property</li></ul></div><pre class="programlisting">contracts {
contractsDslDir = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> File(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${buildDir}/unpackedContracts"</span>)
}</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_do_i_need_a_binary_storage_can_t_i_use_git" href="#_do_i_need_a_binary_storage_can_t_i_use_git"></a>87.6&nbsp;Do I need a Binary Storage? Can&#8217;t I use Git?</h2></div></div></div><p>In the polyglot world, there are languages that don&#8217;t use binary storages like
Artifactory or Nexus. Starting from Spring Cloud Contract version 2.0.0 we provide
mechanisms to store contracts and stubs in a SCM repository. Currently the
only supported SCM is Git.</p><p>The repository would have to the following setup
(which you can checkout <a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/2.1.x/contracts_git/" target="_top">here</a>):</p><pre class="screen">.
&#9492;&#9472;&#9472; META-INF
&#9492;&#9472;&#9472; com.example
&#9492;&#9472;&#9472; beer-api-producer-git
&#9492;&#9472;&#9472; 0.0.1-SNAPSHOT
&#9500;&#9472;&#9472; contracts
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; beer-api-consumer
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; messaging
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; shouldSendAcceptedVerification.groovy
&#9474;&nbsp;&nbsp; &#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; shouldSendRejectedVerification.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; rest
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; shouldGrantABeerIfOldEnough.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; shouldRejectABeerIfTooYoung.groovy
&#9492;&#9472;&#9472; mappings
&#9492;&#9472;&#9472; beer-api-consumer
&#9492;&#9472;&#9472; rest
&#9500;&#9472;&#9472; shouldGrantABeerIfOldEnough.json
&#9492;&#9472;&#9472; shouldRejectABeerIfTooYoung.json</pre><p>Under <code class="literal">META-INF</code> folder:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">we group applications via <code class="literal">groupId</code> (e.g. <code class="literal">com.example</code>)</li><li class="listitem">then each application is represented via the <code class="literal">artifactId</code> (e.g. <code class="literal">beer-api-producer-git</code>)</li><li class="listitem">next, the version of the application. The version is mandatory! (e.g. <code class="literal">0.0.1-SNAPSHOT</code>)</li><li class="listitem"><p class="simpara">finally, there are two folders:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">contracts</code> - the good practice is to store the contracts required by each
consumer in the folder with the consumer name (e.g. <code class="literal">beer-api-consumer</code>). That way you
can use the <code class="literal">stubs-per-consumer</code> feature. Further directory structure is arbitrary.</li><li class="listitem"><code class="literal">mappings</code> - in this folder the Maven / Gradle Spring Cloud Contract plugins will push
the stub server mappings. On the consumer side, Stub Runner will scan this folder
to start stub servers with stub definitions. The folder structure will be a copy
of the one created in the <code class="literal">contracts</code> subfolder.</li></ul></div></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_protocol_convention" href="#_protocol_convention"></a>87.6.1&nbsp;Protocol convention</h3></div></div></div><p>In order to control the type and location of the source of contracts (whether it&#8217;s
a binary storage or an SCM repository), you can use the protocol in the URL of
the repository. Spring Cloud Contract iterates over registered protocol resolvers
and tries to fetch the contracts (via a plugin) or stubs (via Stub Runner).</p><p>For the SCM functionality, currently, we support the Git repository. To use it,
in the property, where the repository URL needs to be placed you just have to prefix
the connection URL with <code class="literal">git://</code>. Here you can find a couple of examples:</p><pre class="screen">git://file:///foo/bar
git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git
git://git@github.com:spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_producer_2" href="#_producer_2"></a>87.6.2&nbsp;Producer</h3></div></div></div><p>For the producer, to use the SCM approach, we can reuse the
same mechanism we use for external contracts. We route Spring Cloud Contract
to use the SCM implementation via the URL that contains
the <code class="literal">git://</code> protocol.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You have to manually add the <code class="literal">pushStubsToScm</code>
goal in Maven or execute (bind) the <code class="literal">pushStubsToScm</code> task in
Gradle. We don&#8217;t push stubs to <code class="literal">origin</code> of your git
repository out of the box.</p></td></tr></table></div><p><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Base class mappings etc. --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- We want to pick contracts from a Git repository --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsRepositoryUrl&gt;</span>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsRepositoryUrl&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- We reuse the contract dependency section to set up the path
to the folder that contains the contract definitions. In our case the
path will be /groupId/artifactId/version/contracts --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>${project.groupId}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>${project.artifactId}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${project.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- The contracts mode can't be classpath --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsMode&gt;</span>REMOTE<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsMode&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;phase&gt;</span>package<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/phase&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- By default we will not push the stubs back to SCM,
you have to explicitly add it as a goal --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>pushStubsToScm<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>
</p><p><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">contracts {
// We want to pick contracts from a Git repository
contractDependency {
stringNotation = "${project.group}:${project.name}:${project.version}"
}
/*
We reuse the contract dependency section to set up the path
to the folder that contains the contract definitions. In our case the
path will be /groupId/artifactId/version/contracts
*/
contractRepository {
repositoryUrl = "git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git"
}
// The mode can't be classpath
contractsMode = "REMOTE"
// Base class mappings etc.
}
/*
In this scenario we want to publish stubs to SCM whenever
the `publish` task is executed
*/
publish.dependsOn("publishStubsToScm")</pre><p>
</p><p>With such a setup:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Git project will be cloned to a temporary directory</li><li class="listitem">The SCM stub downloader will go to <code class="literal">META-INF/groupId/artifactId/version/contracts</code> folder
to find contracts. E.g. for <code class="literal">com.example:foo:1.0.0</code> the path would be
<code class="literal">META-INF/com.example/foo/1.0.0/contracts</code></li><li class="listitem">Tests will be generated from the contracts</li><li class="listitem">Stubs will be created from the contracts</li><li class="listitem">Once the tests pass, the stubs will be committed in the cloned repository</li><li class="listitem">Finally, a push will be done to that repo&#8217;s <code class="literal">origin</code></li></ul></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_keeping_contracts_with_the_producer_and_stubs_in_an_external_repository" href="#_keeping_contracts_with_the_producer_and_stubs_in_an_external_repository"></a>Keeping contracts with the producer and stubs in an external repository</h4></div></div></div><p>It is also possible to keep the contracts in the producer repository, but keep the stubs in an external git repo.
This is most useful when you want to use the base consumer-producer collaboration flow, but do not have a possibility to
use an artifact repository for storing the stubs.</p><p>In order to do that, use the usual producer setup, and then add the <code class="literal">pushStubsToScm</code> goal and set
<code class="literal">contractsRepositoryUrl</code> to the repository where you want to keep the stubs.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_consumer_2" href="#_consumer_2"></a>87.6.3&nbsp;Consumer</h3></div></div></div><p>On the consumer side when passing the <code class="literal">repositoryRoot</code> parameter,
either from the <code class="literal">@AutoConfigureStubRunner</code> annotation, the
JUnit rule, JUnit 5 extension or properties, it&#8217;s enough to pass the URL of the
SCM repository, prefixed with the protocol. For example</p><pre class="programlisting">@AutoConfigureStubRunner(
stubsMode=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"REMOTE"</span>,
repositoryRoot=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git"</span>,
ids=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:bookstore:0.0.1.RELEASE"</span>
)</pre><p>With such a setup:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Git project will be cloned to a temporary directory</li><li class="listitem">The SCM stub downloader will go to <code class="literal">META-INF/groupId/artifactId/version/</code> folder
to find stub definitions and contracts. E.g. for <code class="literal">com.example:foo:1.0.0</code> the path would be
<code class="literal">META-INF/com.example/foo/1.0.0/</code></li><li class="listitem">Stub servers will be started and fed with mappings</li><li class="listitem">Messaging definitions will be read and used in the messaging tests</li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_can_i_use_the_pact_broker" href="#_can_i_use_the_pact_broker"></a>87.7&nbsp;Can I use the Pact Broker?</h2></div></div></div><p>When using <a class="link" href="http://pact.io/" target="_top">Pact</a> you can use the <a class="link" href="https://github.com/pact-foundation/pact_broker" target="_top">Pact Broker</a>
to store and share Pact definitions. Starting from Spring Cloud Contract
2.0.0 one can fetch Pact files from the Pact Broker to generate
tests and stubs.</p><p>As a prerequisite the Pact Converter and Pact Stub Downloader
are required. You have to add them via the <code class="literal">spring-cloud-contract-pact</code> dependency.
You can read more about it in the <a class="xref" href="#pact-converter" title="94.1.1&nbsp;Pact Converter">Section&nbsp;94.1.1, &#8220;Pact Converter&#8221;</a> section.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Pact follows the Consumer Contract convention. That means
that the Consumer creates the Pact definitions first, then
shares the files with the Producer. Those expectations are generated
from the Consumer&#8217;s code and can break the Producer if the expectations
are not met.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_pact_consumer" href="#_pact_consumer"></a>87.7.1&nbsp;Pact Consumer</h3></div></div></div><p>The consumer uses Pact framework to generate Pact files. The
Pact files are sent to the Pact Broker. An example of such
setup can be found <a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/2.1.x/consumer_pact" target="_top">here</a>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_producer_3" href="#_producer_3"></a>87.7.2&nbsp;Producer</h3></div></div></div><p>For the producer, to use the Pact files from the Pact Broker, we can reuse the
same mechanism we use for external contracts. We route Spring Cloud Contract
to use the Pact implementation via the URL that contains
the <code class="literal">pact://</code> protocol. It&#8217;s enough to pass the URL to the
Pact Broker. An example of such setup can be found <a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/2.1.x/producer_pact" target="_top">here</a>.</p><p><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Base class mappings etc. --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- We want to pick contracts from a Git repository --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsRepositoryUrl&gt;</span>pact://http://localhost:8085<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsRepositoryUrl&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- We reuse the contract dependency section to set up the path
to the folder that contains the contract definitions. In our case the
path will be /groupId/artifactId/version/contracts --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>${project.groupId}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>${project.artifactId}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- When + is passed, a latest tag will be applied when fetching pacts --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>+<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- The contracts mode can't be classpath --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsMode&gt;</span>REMOTE<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsMode&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Don't forget to add spring-cloud-contract-pact to the classpath! --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-pact<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>
</p><p><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">buildscript {
repositories {
//...
}
dependencies {
// ...
// Don't forget to add spring-cloud-contract-pact to the classpath!
classpath "org.springframework.cloud:spring-cloud-contract-pact:${contractVersion}"
}
}
contracts {
// When + is passed, a latest tag will be applied when fetching pacts
contractDependency {
stringNotation = "${project.group}:${project.name}:+"
}
contractRepository {
repositoryUrl = "pact://http://localhost:8085"
}
// The mode can't be classpath
contractsMode = "REMOTE"
// Base class mappings etc.
}</pre><p>
</p><p>With such a setup:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Pact files will be downloaded from the Pact Broker</li><li class="listitem">Spring Cloud Contract will convert the Pact files into tests and stubs</li><li class="listitem">The JAR with the stubs gets automatically created as usual</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_pact_consumer_producer_contract_approach" href="#_pact_consumer_producer_contract_approach"></a>87.7.3&nbsp;Pact Consumer (Producer Contract approach)</h3></div></div></div><p>In the scenario where you don&#8217;t want to do Consumer Contract approach
(for every single consumer define the expectations) but you&#8217;d prefer
to do Producer Contracts (the producer provides the contracts and
publishes stubs), it&#8217;s enough to use Spring Cloud Contract with
Stub Runner option. An example of such setup can be found <a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/2.1.x/consumer_pact_stubrunner" target="_top">here</a>.</p><p>First, remember to add Stub Runner and Spring Cloud Contract Pact module
as test dependencies.</p><p><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Don't forget to add spring-cloud-contract-pact to the classpath! --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- ... --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-pact<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span></pre><p>
</p><p><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
dependencies {
//...
testCompile("org.springframework.cloud:spring-cloud-starter-contract-stub-runner")
// Don't forget to add spring-cloud-contract-pact to the classpath!
testCompile("org.springframework.cloud:spring-cloud-contract-pact")
}</pre><p>
</p><p>Next, just pass the URL of the Pact Broker to <code class="literal">repositoryRoot</code>, prefixed
with <code class="literal">pact://</code> protocol. E.g. <code class="literal">pact://http://localhost:8085</code></p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(stubsMode = StubRunnerProperties.StubsMode.REMOTE,
ids = "com.example:beer-api-producer-pact",
repositoryRoot = "pact://http://localhost:8085")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BeerControllerTest {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Inject the port of the running stub</span>
<em><span class="hl-annotation" style="color: gray">@StubRunnerPort("beer-api-producer-pact")</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> producerPort;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}</pre><p>With such a setup:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Pact files will be downloaded from the Pact Broker</li><li class="listitem">Spring Cloud Contract will convert the Pact files into stub definitions</li><li class="listitem">The stub servers will be started and fed with stubs</li></ul></div><p>For more information about Pact support you can go to
the <a class="xref" href="#pact-stub-downloader" title="94.7&nbsp;Using the Pact Stub Downloader">Section&nbsp;94.7, &#8220;Using the Pact Stub Downloader&#8221;</a> section.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_can_i_debug_the_request_response_being_sent_by_the_generated_tests_client" href="#_how_can_i_debug_the_request_response_being_sent_by_the_generated_tests_client"></a>87.8&nbsp;How can I debug the request/response being sent by the generated tests client?</h2></div></div></div><p>The generated tests all boil down to RestAssured in some form or fashion which relies on <a class="link" href="https://hc.apache.org/httpcomponents-client-ga/" target="_top">Apache HttpClient</a>. HttpClient has a facility called <a class="link" href="https://hc.apache.org/httpcomponents-client-ga/logging.html#Wire_Logging" target="_top">wire logging</a> which logs the entire request and response to HttpClient. Spring Boot has a logging <a class="link" href="https://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html" target="_top">common application property</a> for doing this sort of thing, just add this to your application properties</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">logging.level.org.apache.http.wire</span>=DEBUG</pre><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_can_i_debug_the_mapping_request_response_being_sent_by_wiremock" href="#_how_can_i_debug_the_mapping_request_response_being_sent_by_wiremock"></a>87.8.1&nbsp;How can I debug the mapping/request/response being sent by WireMock?</h3></div></div></div><p>Starting from version <code class="literal">1.2.0</code> we turn on WireMock logging to
info and the WireMock notifier to being verbose. Now you will
exactly know what request was received by WireMock server and which
matching response definition was picked.</p><p>To turn off this feature just bump WireMock logging to <code class="literal">ERROR</code></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">logging.level.com.github.tomakehurst.wiremock</span>=ERROR</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_can_i_see_what_got_registered_in_the_http_server_stub" href="#_how_can_i_see_what_got_registered_in_the_http_server_stub"></a>87.8.2&nbsp;How can I see what got registered in the HTTP server stub?</h3></div></div></div><p>You can use the <code class="literal">mappingsOutputFolder</code> property on <code class="literal">@AutoConfigureStubRunner</code>, <code class="literal">StubRunnerRule</code> or
`StubRunnerExtension`to dump all mappings per artifact id. Also the port at which the given stub server
was started will be attached.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_can_i_reference_text_from_file" href="#_can_i_reference_text_from_file"></a>87.8.3&nbsp;Can I reference text from file?</h3></div></div></div><p>Yes! With version 1.2.0 we&#8217;ve added such a possibility. It&#8217;s enough to call <code class="literal">file(&#8230;&#8203;)</code> method in the
DSL and provide a path relative to where the contract lays.
If you&#8217;re using YAML just use the <code class="literal">bodyFromFile</code> property.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_contract_verifier_setup" href="#_spring_cloud_contract_verifier_setup"></a>88.&nbsp;Spring Cloud Contract Verifier Setup</h2></div></div></div><p>You can set up Spring Cloud Contract Verifier in the following ways:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="#gradle-project" title="88.1&nbsp;Gradle Project">As a Gradle project</a></li><li class="listitem"><a class="link" href="#maven-project" title="88.2&nbsp;Maven Project">As a Maven project</a></li><li class="listitem"><a class="link" href="#docker-project" title="88.5&nbsp;Docker Project">As a Docker project</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="gradle-project" href="#gradle-project"></a>88.1&nbsp;Gradle Project</h2></div></div></div><p>To learn how to set up the Gradle project for Spring Cloud Contract Verifier, read the
following sections:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#gradle-prerequisites" title="88.1.1&nbsp;Prerequisites">Section&nbsp;88.1.1, &#8220;Prerequisites&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-add-gradle-plugin" title="88.1.2&nbsp;Add Gradle Plugin with Dependencies">Section&nbsp;88.1.2, &#8220;Add Gradle Plugin with Dependencies&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-and-rest-assured" title="88.1.3&nbsp;Gradle and Rest Assured 2.0">Section&nbsp;88.1.3, &#8220;Gradle and Rest Assured 2.0&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-snapshot-versions" title="88.1.4&nbsp;Snapshot Versions for Gradle">Section&nbsp;88.1.4, &#8220;Snapshot Versions for Gradle&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-add-stubs" title="88.1.5&nbsp;Add stubs">Section&nbsp;88.1.5, &#8220;Add stubs&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-default-setup" title="88.1.7&nbsp;Default Setup">Section&nbsp;88.1.7, &#8220;Default Setup&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-configure-plugin" title="88.1.8&nbsp;Configure Plugin">Section&nbsp;88.1.8, &#8220;Configure Plugin&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-configuration-options" title="88.1.9&nbsp;Configuration Options">Section&nbsp;88.1.9, &#8220;Configuration Options&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-single-base-class" title="88.1.10&nbsp;Single Base Class for All Tests">Section&nbsp;88.1.10, &#8220;Single Base Class for All Tests&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-different-base-classes" title="88.1.11&nbsp;Different Base Classes for Contracts">Section&nbsp;88.1.11, &#8220;Different Base Classes for Contracts&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-invoking-generated-tests" title="88.1.12&nbsp;Invoking Generated Tests">Section&nbsp;88.1.12, &#8220;Invoking Generated Tests&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-pushing-stubs-to-scm" title="88.1.13&nbsp;Pushing stubs to SCM">Section&nbsp;88.1.13, &#8220;Pushing stubs to SCM&#8221;</a></li><li class="listitem"><a class="xref" href="#gradle-consumer" title="88.1.14&nbsp;Spring Cloud Contract Verifier on the Consumer Side">Section&nbsp;88.1.14, &#8220;Spring Cloud Contract Verifier on the Consumer Side&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-prerequisites" href="#gradle-prerequisites"></a>88.1.1&nbsp;Prerequisites</h3></div></div></div><p>In order to use Spring Cloud Contract Verifier with WireMock, you muse use either a
Gradle or a Maven plugin.</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>If you want to use Spock in your projects, you must add separately the
<code class="literal">spock-core</code> and <code class="literal">spock-spring</code> modules. Check <a class="link" href="http://spockframework.github.io/" target="_top">Spock
docs for more information</a></p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-add-gradle-plugin" href="#gradle-add-gradle-plugin"></a>88.1.2&nbsp;Add Gradle Plugin with Dependencies</h3></div></div></div><p>To add a Gradle plugin with dependencies, use code similar to this:</p><pre class="programlisting">buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifier_version}"</span>
}
}
apply plugin: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'groovy'</span>
apply plugin: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'spring-cloud-contract'</span>
dependencyManagement {
imports {
mavenBom <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-dependencies:${verifier_version}"</span>
}
}
dependencies {
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.codehaus.groovy:groovy-all:2.4.6'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// example with adding Spock core and Spock Spring</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.spockframework:spock-core:1.0-groovy-2.4'</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.spockframework:spock-spring:1.0-groovy-2.4'</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud:spring-cloud-starter-contract-verifier'</span>
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-and-rest-assured" href="#gradle-and-rest-assured"></a>88.1.3&nbsp;Gradle and Rest Assured 2.0</h3></div></div></div><p>By default, Rest Assured 3.x is added to the classpath. However, to use Rest Assured 2.x
you can add it to the plugins classpath, as shown here:</p><pre class="programlisting">buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.boot:spring-boot-gradle-plugin:${springboot_version}"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-gradle-plugin:${verifier_version}"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.jayway.restassured:rest-assured:2.5.0"</span>
classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.jayway.restassured:spring-mock-mvc:2.5.0"</span>
}
}
depenendencies {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// all dependencies</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// you can exclude rest-assured from spring-cloud-contract-verifier</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.jayway.restassured:rest-assured:2.5.0"</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.jayway.restassured:spring-mock-mvc:2.5.0"</span>
}</pre><p>That way, the plugin automatically sees that Rest Assured 2.x is present on the classpath
and modifies the imports accordingly.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-snapshot-versions" href="#gradle-snapshot-versions"></a>88.1.4&nbsp;Snapshot Versions for Gradle</h3></div></div></div><p>Add the additional snapshot repository to your build.gradle to use snapshot versions,
which are automatically uploaded after every successful build, as shown here:</p><pre class="programlisting">buildscript {
repositories {
mavenCentral()
mavenLocal()
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/snapshot"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/milestone"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/release"</span> }
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-add-stubs" href="#gradle-add-stubs"></a>88.1.5&nbsp;Add stubs</h3></div></div></div><p>By default, Spring Cloud Contract Verifier is looking for stubs in the
<code class="literal">src/test/resources/contracts</code> directory.</p><p>The directory containing stub definitions is treated as a class name, and each stub
definition is treated as a single test. Spring Cloud Contract Verifier assumes that it
contains at least one level of directories that are to be used as the test class name.
If more than one level of nested directories is present, all except the last one is used
as the package name. For example, with following structure:</p><pre class="programlisting">src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy</pre><p>Spring Cloud Contract Verifier creates a test class named <code class="literal">defaultBasePackage.MyService</code>
with two methods:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">shouldCreateUser()</code></li><li class="listitem"><code class="literal">shouldReturnUser()</code></li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-run-plugin" href="#gradle-run-plugin"></a>88.1.6&nbsp;Run the Plugin</h3></div></div></div><p>The plugin registers itself to be invoked before a <code class="literal">check</code> task. If you want it to be
part of your build process, you need to do nothing more. If you just want to generate
tests, invoke the <code class="literal">generateContractTests</code> task.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-default-setup" href="#gradle-default-setup"></a>88.1.7&nbsp;Default Setup</h3></div></div></div><p>The default Gradle Plugin setup creates the following Gradle part of the build (in
pseudocode):</p><pre class="programlisting">contracts {
testFramework =<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'JUNIT'</span>
testMode = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'MockMvc'</span>
generatedTestSourcesDir = project.file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/generated-test-sources/contracts"</span>)
contractsDslDir = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.rootDir}/src/test/resources/contracts"</span>
basePackageForTests = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.verifier.tests'</span>
stubsOutputDir = project.file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/stubs"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the following properties are used when you want to provide where the JAR with contract lays</span>
contractDependency {
stringNotation = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>
}
contractsPath = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>
contractsWorkOffline = false
contractRepository {
cacheDownloadedContracts(true)
}
}
tasks.create(type: Jar, name: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'verifierStubsJar'</span>, dependsOn: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'generateClientStubs'</span>) {
baseName = project.name
classifier = contracts.stubsSuffix
from contractVerifier.stubsOutputDir
}
project.artifacts {
archives task
}
tasks.create(type: Copy, name: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'copyContracts'</span>) {
from contracts.contractsDslDir
into contracts.stubsOutputDir
}
verifierStubsJar.dependsOn <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'copyContracts'</span>
publishing {
publications {
stubs(MavenPublication) {
artifactId project.name
artifact verifierStubsJar
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-configure-plugin" href="#gradle-configure-plugin"></a>88.1.8&nbsp;Configure Plugin</h3></div></div></div><p>To change the default configuration, add a <code class="literal">contracts</code> snippet to your Gradle config, as
shown here:</p><pre class="programlisting">contracts {
testMode = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'MockMvc'</span>
baseClassForTests = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.mycompany.tests'</span>
generatedTestSourcesDir = project.file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'src/generatedContract'</span>)
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-configuration-options" href="#gradle-configuration-options"></a>88.1.9&nbsp;Configuration Options</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>testMode</strong></span>: Defines the mode for acceptance tests. By default, the mode is MockMvc,
which is based on Spring&#8217;s MockMvc. It can also be changed to <span class="strong"><strong>WebTestClient</strong></span>, <span class="strong"><strong>JaxRsClient</strong></span> or to
<span class="strong"><strong>Explicit</strong></span> for real HTTP calls.</li><li class="listitem"><span class="strong"><strong>imports</strong></span>: Creates an array with imports that should be included in generated tests
(for example ['org.myorg.Matchers']). By default, it creates an empty array.</li><li class="listitem"><span class="strong"><strong>staticImports</strong></span>: Creates an array with static imports that should be included in
generated tests(for example ['org.myorg.Matchers.*']). By default, it creates an empty
array.</li><li class="listitem"><span class="strong"><strong>basePackageForTests</strong></span>: Specifies the base package for all generated tests. If not set,
the value is picked from <code class="literal">baseClassForTests&#8217;s package and from `packageWithBaseClasses</code>.
If neither of these values are set, then the value is set to
<code class="literal">org.springframework.cloud.contract.verifier.tests</code>.</li><li class="listitem"><span class="strong"><strong>baseClassForTests</strong></span>: Creates a base class for all generated tests. By default, if you
use Spock classes, the class is <code class="literal">spock.lang.Specification</code>.</li><li class="listitem"><span class="strong"><strong>packageWithBaseClasses</strong></span>: Defines a package where all the base classes reside. This
setting takes precedence over <span class="strong"><strong>baseClassForTests</strong></span>.</li><li class="listitem"><span class="strong"><strong>baseClassMappings</strong></span>: Explicitly maps a contract package to a FQN of a base class. This
setting takes precedence over <span class="strong"><strong>packageWithBaseClasses</strong></span> and <span class="strong"><strong>baseClassForTests</strong></span>.</li><li class="listitem"><span class="strong"><strong>ruleClassForTests</strong></span>: Specifies a rule that should be added to the generated test
classes.</li><li class="listitem"><span class="strong"><strong>ignoredFiles</strong></span>: Uses an <code class="literal">Antmatcher</code> to allow defining stub files for which processing
should be skipped. By default, it is an empty array.</li><li class="listitem"><span class="strong"><strong>contractsDslDir</strong></span>: Specifies the directory containing contracts written using the
GroovyDSL. By default, its value is <code class="literal">$rootDir/src/test/resources/contracts</code>.</li><li class="listitem"><span class="strong"><strong>generatedTestSourcesDir</strong></span>: Specifies the test source directory where tests generated
from the Groovy DSL should be placed. By default its value is
<code class="literal">$buildDir/generated-test-sources/contractVerifier</code>.</li><li class="listitem"><span class="strong"><strong>stubsOutputDir</strong></span>: Specifies the directory where the generated WireMock stubs from
the Groovy DSL should be placed.</li><li class="listitem"><span class="strong"><strong>testFramework</strong></span>: Specifies the target test framework to be used. Currently, Spock, JUnit 4 (<code class="literal">TestFramework.JUNIT</code> and
JUnit 5 are supported with JUnit 4 being the default framework.</li><li class="listitem"><span class="strong"><strong>contractsProperties</strong></span>: a map containing properties to be passed to Spring Cloud Contract
components. Those properties might be used by e.g. inbuilt or custom Stub Downloaders.</li></ul></div><p>The following properties are used when you want to specify the location of the JAR
containing the contracts:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>contractDependency</strong></span>: Specifies the Dependency that provides
<code class="literal">groupid:artifactid:version:classifier</code> coordinates. You can use the <code class="literal">contractDependency</code>
closure to set it up.</li><li class="listitem"><span class="strong"><strong>contractsPath</strong></span>: Specifies the path to the jar. If contract dependencies are
downloaded, the path defaults to <code class="literal">groupid/artifactid</code> where <code class="literal">groupid</code> is slash
separated. Otherwise, it scans contracts under the provided directory.</li><li class="listitem"><span class="strong"><strong>contractsMode</strong></span>: Specifies the mode of downloading contracts (whether the
JAR is available offline, remotely etc.)</li><li class="listitem"><span class="strong"><strong>deleteStubsAfterTest</strong></span>: If set to <code class="literal">false</code> will not remove any downloaded
contracts from temporary directories</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-single-base-class" href="#gradle-single-base-class"></a>88.1.10&nbsp;Single Base Class for All Tests</h3></div></div></div><p>When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base
specification for all generated acceptance tests. In this class, you need to point to an
endpoint, which should be verified.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BaseMockMvcSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> PairIdController())
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> isProperCorrelationId(Integer correlationId) {
assert correlationId == <span class="hl-number">123456</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> isEmpty(String value) {
assert value == null
}
}</pre><p>If you use <code class="literal">Explicit</code> mode, you can use a base class to initialize the whole tested app
as you might see in regular integration tests. If you use the <code class="literal">JAXRSCLIENT</code> mode, this
base class should also contain a <code class="literal">protected WebTarget webTarget</code> field. Right now, the
only option to test the JAX-RS API is to start a web server.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-different-base-classes" href="#gradle-different-base-classes"></a>88.1.11&nbsp;Different Base Classes for Contracts</h3></div></div></div><p>If your base classes differ between contracts, you can tell the Spring Cloud Contract
plugin which class should get extended by the autogenerated tests. You have two options:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Follow a convention by providing the <code class="literal">packageWithBaseClasses</code></li><li class="listitem">Provide explicit mapping via <code class="literal">baseClassMappings</code></li></ul></div><p><span class="strong"><strong>By Convention</strong></span></p><p>The convention is such that if you have a contract under (for example)
<code class="literal">src/test/resources/contract/foo/bar/baz/</code> and set the value of the
<code class="literal">packageWithBaseClasses</code> property to <code class="literal">com.example.base</code>, then Spring Cloud Contract
Verifier assumes that there is a <code class="literal">BarBazBase</code> class under the <code class="literal">com.example.base</code> package.
In other words, the system takes the last two parts of the package, if they exist, and
forms a class with a <code class="literal">Base</code> suffix. This rule takes precedence over <span class="strong"><strong>baseClassForTests</strong></span>.
Here is an example of how it works in the <code class="literal">contracts</code> closure:</p><pre class="programlisting">packageWithBaseClasses = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example.base'</span></pre><p><span class="strong"><strong>By Mapping</strong></span></p><p>You can manually map a regular expression of the contract&#8217;s package to fully qualified
name of the base class for the matched contract. You have to provide a list called
<code class="literal">baseClassMappings</code> that consists <code class="literal">baseClassMapping</code> objects that takes a
<code class="literal">contractPackageRegex</code> to <code class="literal">baseClassFQN</code> mapping. Consider the following example:</p><pre class="programlisting">baseClassForTests = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example.FooBase"</span>
baseClassMappings {
baseClassMapping(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'.*/com/.*'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example.ComBase'</span>)
baseClassMapping(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'.*/bar/.*'</span>:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'com.example.BarBase'</span>)
}</pre><p>Let&#8217;s assume that you have contracts under
- <code class="literal">src/test/resources/contract/com/</code>
- <code class="literal">src/test/resources/contract/foo/</code></p><p>By providing the <code class="literal">baseClassForTests</code>, we have a fallback in case mapping did not succeed.
(You could also provide the <code class="literal">packageWithBaseClasses</code> as a fallback.) That way, the tests
generated from <code class="literal">src/test/resources/contract/com/</code> contracts extend the
<code class="literal">com.example.ComBase</code>, whereas the rest of the tests extend <code class="literal">com.example.FooBase</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-invoking-generated-tests" href="#gradle-invoking-generated-tests"></a>88.1.12&nbsp;Invoking Generated Tests</h3></div></div></div><p>To ensure that the provider side is compliant with defined contracts, you need to invoke:</p><pre class="programlisting">./gradlew generateContractTests <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-pushing-stubs-to-scm" href="#gradle-pushing-stubs-to-scm"></a>88.1.13&nbsp;Pushing stubs to SCM</h3></div></div></div><p>If you&#8217;re using the SCM repository to keep the contracts and
stubs, you might want to automate the step of pushing stubs to
the repository. To do that, it&#8217;s enough to call the <code class="literal">pushStubsToScm</code>
task. Example:</p><pre class="programlisting">$ ./gradlew pushStubsToScm</pre><p>Under <a class="xref" href="#scm-stub-downloader" title="94.6&nbsp;Using the SCM Stub Downloader">Section&nbsp;94.6, &#8220;Using the SCM Stub Downloader&#8221;</a> you can find all possible
configuration options that you can pass either via
the <code class="literal">contractsProperties</code> field e.g. <code class="literal">contracts { contractsProperties = [foo:"bar"] }</code>,
via <code class="literal">contractsProperties</code> method e.g. <code class="literal">contracts { contractsProperties([foo:"bar"]) }</code>,
a system property or an environment variable.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="gradle-consumer" href="#gradle-consumer"></a>88.1.14&nbsp;Spring Cloud Contract Verifier on the Consumer Side</h3></div></div></div><p>In a consuming service, you need to configure the Spring Cloud Contract Verifier plugin
in exactly the same way as in case of provider. If you do not want to use Stub Runner
then you need to copy contracts stored in <code class="literal">src/test/resources/contracts</code> and generate
WireMock JSON stubs using:</p><pre class="programlisting">./gradlew generateClientStubs</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">stubsOutputDir</code> option has to be set for stub generation to work.</p></td></tr></table></div><p>When present, JSON stubs can be used in automated tests of consuming a service.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(loader == SpringApplicationContextLoader, classes == Application)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> LoanApplicationServiceSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
<em><span class="hl-annotation" style="color: gray">@ClassRule</span></em>
<em><span class="hl-annotation" style="color: gray">@Shared</span></em>
WireMockClassRule wireMockRule == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WireMockClassRule()
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
LoanApplicationService sut
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should successfully apply for loan'</span>() {
given:
LoanApplication application =
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> LoanApplication(client: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Client(clientPesel: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'12345678901'</span>), amount: <span class="hl-number">123.123</span>)
when:
LoanApplicationResult loanApplication == sut.loanApplication(application)
then:
loanApplication.loanApplicationStatus == LoanApplicationStatus.LOAN_APPLIED
loanApplication.rejectionReason == null
}
}</pre><p><code class="literal">LoanApplication</code> makes a call to <code class="literal">FraudDetection</code> service. This request is handled by a
WireMock server configured with stubs generated by Spring Cloud Contract Verifier.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="maven-project" href="#maven-project"></a>88.2&nbsp;Maven Project</h2></div></div></div><p>To learn how to set up the Maven project for Spring Cloud Contract Verifier, read the
following sections:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#maven-add-plugin" title="88.2.1&nbsp;Add maven plugin">Section&nbsp;88.2.1, &#8220;Add maven plugin&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-rest-assured" title="88.2.2&nbsp;Maven and Rest Assured 2.0">Section&nbsp;88.2.2, &#8220;Maven and Rest Assured 2.0&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-snapshot-versions" title="88.2.3&nbsp;Snapshot versions for Maven">Section&nbsp;88.2.3, &#8220;Snapshot versions for Maven&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-add-stubs" title="88.2.4&nbsp;Add stubs">Section&nbsp;88.2.4, &#8220;Add stubs&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-run-plugin" title="88.2.5&nbsp;Run plugin">Section&nbsp;88.2.5, &#8220;Run plugin&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-configure-plugin" title="88.2.6&nbsp;Configure plugin">Section&nbsp;88.2.6, &#8220;Configure plugin&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-configuration-options" title="88.2.7&nbsp;Configuration Options">Section&nbsp;88.2.7, &#8220;Configuration Options&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-single-base" title="88.2.8&nbsp;Single Base Class for All Tests">Section&nbsp;88.2.8, &#8220;Single Base Class for All Tests&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-different-base" title="88.2.9&nbsp;Different base classes for contracts">Section&nbsp;88.2.9, &#8220;Different base classes for contracts&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-invoking-generated-tests" title="88.2.10&nbsp;Invoking generated tests">Section&nbsp;88.2.10, &#8220;Invoking generated tests&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-pushing-stubs-to-scm" title="88.2.11&nbsp;Pushing stubs to SCM">Section&nbsp;88.2.11, &#8220;Pushing stubs to SCM&#8221;</a></li><li class="listitem"><a class="xref" href="#maven-sts" title="88.2.12&nbsp;Maven Plugin and STS">Section&nbsp;88.2.12, &#8220;Maven Plugin and STS&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-add-plugin" href="#maven-add-plugin"></a>88.2.1&nbsp;Add maven plugin</h3></div></div></div><p>Add the Spring Cloud Contract BOM in a fashion similar to this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-release.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span></pre><p>Next, add the <code class="literal">Spring Cloud Contract Verifier</code> Maven plugin:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example.fraud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>You can read more in the
<a class="link" href="https://cloud.spring.io/spring-cloud-static/spring-cloud-contract/2.0.0.RELEASE/spring-cloud-contract-maven-plugin/" target="_top">Spring
Cloud Contract Maven Plugin Documentation (example for <code class="literal">2.0.0.RELEASE</code> version)</a>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-rest-assured" href="#maven-rest-assured"></a>88.2.2&nbsp;Maven and Rest Assured 2.0</h3></div></div></div><p>By default, Rest Assured 3.x is added to the classpath. However, you can use Rest
Assured 2.x by adding it to the plugins classpath, as shown here:</p><pre class="programlisting">&lt;plugin&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-contract-maven-plugin&lt;/artifactId&gt;
&lt;version&gt;${spring-cloud-contract.version}&lt;/version&gt;
&lt;extensions&gt;true&lt;/extensions&gt;
&lt;configuration&gt;
&lt;packageWithBaseClasses&gt;com.example&lt;/packageWithBaseClasses&gt;
&lt;/configuration&gt;
&lt;dependencies&gt;
&lt;dependency&gt;
&lt;groupId&gt;org.springframework.cloud&lt;/groupId&gt;
&lt;artifactId&gt;spring-cloud-contract-verifier&lt;/artifactId&gt;
&lt;version&gt;${spring-cloud-contract.version}&lt;/version&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.jayway.restassured&lt;/groupId&gt;
&lt;artifactId&gt;rest-assured&lt;/artifactId&gt;
&lt;version&gt;<span class="hl-number">2.5</span>.<span class="hl-number">0</span>&lt;/version&gt;
&lt;scope&gt;compile&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.jayway.restassured&lt;/groupId&gt;
&lt;artifactId&gt;spring-mock-mvc&lt;/artifactId&gt;
&lt;version&gt;<span class="hl-number">2.5</span>.<span class="hl-number">0</span>&lt;/version&gt;
&lt;scope&gt;compile&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;
&lt;/plugin&gt;
&lt;dependencies&gt;
&lt;!-- all dependencies --&gt;
&lt;!-- you can exclude rest-assured from spring-cloud-contract-verifier --&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.jayway.restassured&lt;/groupId&gt;
&lt;artifactId&gt;rest-assured&lt;/artifactId&gt;
&lt;version&gt;<span class="hl-number">2.5</span>.<span class="hl-number">0</span>&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;dependency&gt;
&lt;groupId&gt;com.jayway.restassured&lt;/groupId&gt;
&lt;artifactId&gt;spring-mock-mvc&lt;/artifactId&gt;
&lt;version&gt;<span class="hl-number">2.5</span>.<span class="hl-number">0</span>&lt;/version&gt;
&lt;scope&gt;test&lt;/scope&gt;
&lt;/dependency&gt;
&lt;/dependencies&gt;</pre><p>That way, the plugin automatically sees that Rest Assured 3.x is present on the classpath
and modifies the imports accordingly.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-snapshot-versions" href="#maven-snapshot-versions"></a>88.2.3&nbsp;Snapshot versions for Maven</h3></div></div></div><p>For Snapshot and Milestone versions, you have to add the following section to your
<code class="literal">pom.xml</code>, as shown here:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepositories&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-add-stubs" href="#maven-add-stubs"></a>88.2.4&nbsp;Add stubs</h3></div></div></div><p>By default, Spring Cloud Contract Verifier is looking for stubs in the
<code class="literal">src/test/resources/contracts</code> directory. The directory containing stub definitions is
treated as a class name, and each stub definition is treated as a single test. We assume
that it contains at least one directory to be used as test class name. If there is more
than one level of nested directories, all except the last one is used as package name.
For example, with following structure:</p><pre class="programlisting">src/test/resources/contracts/myservice/shouldCreateUser.groovy
src/test/resources/contracts/myservice/shouldReturnUser.groovy</pre><p>Spring Cloud Contract Verifier creates a test class named <code class="literal">defaultBasePackage.MyService</code>
with two methods</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">shouldCreateUser()</code></li><li class="listitem"><code class="literal">shouldReturnUser()</code></li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-run-plugin" href="#maven-run-plugin"></a>88.2.5&nbsp;Run plugin</h3></div></div></div><p>The plugin goal <code class="literal">generateTests</code> is assigned to be invoked in the phase called
<code class="literal">generate-test-sources</code>. If you want it to be part of your build process, you need not do
anything. If you just want to generate tests, invoke the <code class="literal">generateTests</code> goal.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-configure-plugin" href="#maven-configure-plugin"></a>88.2.6&nbsp;Configure plugin</h3></div></div></div><p>To change the default configuration, just add a <code class="literal">configuration</code> section to the plugin
definition or the <code class="literal">execution</code> definition, as shown here:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>convert<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>generateStubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>generateTests<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;basePackageForTests&gt;</span>org.springframework.cloud.verifier.twitter.place<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/basePackageForTests&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassForTests&gt;</span>org.springframework.cloud.verifier.twitter.place.BaseMockMvcSpec<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassForTests&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-configuration-options" href="#maven-configuration-options"></a>88.2.7&nbsp;Configuration Options</h3></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>testMode</strong></span>: Defines the mode for acceptance tests. By default, the mode is MockMvc,
which is based on Spring&#8217;s MockMvc. It can also be changed to <span class="strong"><strong>WebTestClient</strong></span>, <span class="strong"><strong>JaxRsClient</strong></span> or to
<span class="strong"><strong>Explicit</strong></span> for real HTTP calls.</li><li class="listitem"><span class="strong"><strong>basePackageForTests</strong></span>: Specifies the base package for all generated tests. If not set,
the value is picked from <code class="literal">baseClassForTests&#8217;s package and from `packageWithBaseClasses</code>.
If neither of these values are set, then the value is set to
<code class="literal">org.springframework.cloud.contract.verifier.tests</code>.</li><li class="listitem"><span class="strong"><strong>ruleClassForTests</strong></span>: Specifies a rule that should be added to the generated test
classes.</li><li class="listitem"><span class="strong"><strong>baseClassForTests</strong></span>: Creates a base class for all generated tests. By default, if you
use Spock classes, the class is <code class="literal">spock.lang.Specification</code>.</li><li class="listitem"><span class="strong"><strong>contractsDirectory</strong></span>: Specifies a directory containing contracts written with the
GroovyDSL. The default directory is <code class="literal">/src/test/resources/contracts</code>.</li><li class="listitem"><span class="strong"><strong>testFramework</strong></span>: Specifies the target test framework to be used. Currently, Spock, JUnit 4 (<code class="literal">TestFramework.JUNIT</code> and
6JUnit 5 are supported with JUnit 4 being the default framework.</li><li class="listitem"><span class="strong"><strong>packageWithBaseClasses</strong></span>: Defines a package where all the base classes reside. This
setting takes precedence over <span class="strong"><strong>baseClassForTests</strong></span>. The convention is such that, if you
have a contract under (for example) <code class="literal">src/test/resources/contract/foo/bar/baz/</code> and set
the value of the <code class="literal">packageWithBaseClasses</code> property to <code class="literal">com.example.base</code>, then Spring
Cloud Contract Verifier assumes that there is a <code class="literal">BarBazBase</code> class under the
<code class="literal">com.example.base</code> package. In other words, the system takes the last two parts of the
package, if they exist, and forms a class with a <code class="literal">Base</code> suffix.</li><li class="listitem"><span class="strong"><strong>baseClassMappings</strong></span>: Specifies a list of base class mappings that provide
<code class="literal">contractPackageRegex</code>, which is checked against the package where the contract is
located, and <code class="literal">baseClassFQN</code>, which maps to the fully qualified name of the base class for
the matched contract. For example, if you have a contract under
<code class="literal">src/test/resources/contract/foo/bar/baz/</code> and map the property
<code class="literal">.* &#8594; com.example.base.BaseClass</code>, then the test class generated from these contracts
extends <code class="literal">com.example.base.BaseClass</code>. This setting takes precedence over
<span class="strong"><strong>packageWithBaseClasses</strong></span> and <span class="strong"><strong>baseClassForTests</strong></span>.</li><li class="listitem"><span class="strong"><strong>contractsProperties</strong></span>: a map containing properties to be passed to Spring Cloud Contract
components. Those properties might be used by e.g. inbuilt or custom Stub Downloaders.</li></ul></div><p>If you want to download your contract definitions from a Maven repository, you can use
the following options:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>contractDependency</strong></span>: The contract dependency that contains all the packaged contracts.</li><li class="listitem"><span class="strong"><strong>contractsPath</strong></span>: The path to the concrete contracts in the JAR with packaged contracts.
Defaults to <code class="literal">groupid/artifactid</code> where <code class="literal">gropuid</code> is slash separated.</li><li class="listitem"><span class="strong"><strong>contractsMode</strong></span>: Picks the mode in which stubs will be found and registered</li><li class="listitem"><span class="strong"><strong>deleteStubsAfterTest</strong></span>: If set to <code class="literal">false</code> will not remove any downloaded
contracts from temporary directories</li><li class="listitem"><span class="strong"><strong>contractsRepositoryUrl</strong></span>: URL to a repo with the artifacts that have contracts. If it is not provided,
use the current Maven ones.</li><li class="listitem"><span class="strong"><strong>contractsRepositoryUsername</strong></span>: The user name to be used to connect to the repo with contracts.</li><li class="listitem"><span class="strong"><strong>contractsRepositoryPassword</strong></span>: The password to be used to connect to the repo with contracts.</li><li class="listitem"><span class="strong"><strong>contractsRepositoryProxyHost</strong></span>: The proxy host to be used to connect to the repo with contracts.</li><li class="listitem"><span class="strong"><strong>contractsRepositoryProxyPort</strong></span>: The proxy port to be used to connect to the repo with contracts.</li></ul></div><p>We cache only non-snapshot, explicitly provided versions (for example
<code class="literal">+</code> or <code class="literal">Greenwich.M3</code> won&#8217;t get cached). By default, this feature is turned on.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-single-base" href="#maven-single-base"></a>88.2.8&nbsp;Single Base Class for All Tests</h3></div></div></div><p>When using Spring Cloud Contract Verifier in default MockMvc, you need to create a base
specification for all generated acceptance tests. In this class, you need to point to an
endpoint, which should be verified.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.mycompany.tests
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.mycompany.ExampleSpringController
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.restassured.module.mockmvc.RestAssuredMockMvc
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> spock.lang.Specification
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MvcSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ExampleSpringController())
}
}</pre><p>You can also setup the whole context if necessary.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> io.restassured.module.mockmvc.RestAssuredMockMvc;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Before;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.runner.RunWith;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.boot.test.context.SpringBootTest;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.test.context.junit4.SpringRunner;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.web.context.WebApplicationContext;
<em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SomeConfig.class, properties="some=property")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BaseTestClass {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
WebApplicationContext context;
<em><span class="hl-annotation" style="color: gray">@Before</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setup() {
RestAssuredMockMvc.webAppContextSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.context);
}
}</pre><p>If you use <code class="literal">EXPLICIT</code> mode, you can use a base class to initialize the whole tested app
similarly, as you might find in regular integration tests.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> io.restassured.RestAssured;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Before;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.runner.RunWith;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.boot.test.context.SpringBootTest;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.boot.web.server.LocalServerPort
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.test.context.junit4.SpringRunner;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.web.context.WebApplicationContext;
<em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = SomeConfig.class, properties="some=property")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BaseTestClass {
<em><span class="hl-annotation" style="color: gray">@LocalServerPort</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port;
<em><span class="hl-annotation" style="color: gray">@Before</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setup() {
RestAssured.baseURI = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:"</span> + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.port;
}
}</pre><p>If you use the <code class="literal">JAXRSCLIENT</code> mode, this base class should also contain a <code class="literal">protected WebTarget webTarget</code> field. Right
now, the only option to test the JAX-RS API is to start a web server.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-different-base" href="#maven-different-base"></a>88.2.9&nbsp;Different base classes for contracts</h3></div></div></div><p>If your base classes differ between contracts, you can tell the Spring Cloud Contract
plugin which class should get extended by the autogenerated tests. You have two options:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Follow a convention by providing the <code class="literal">packageWithBaseClasses</code></li><li class="listitem">provide explicit mapping via <code class="literal">baseClassMappings</code></li></ul></div><p><span class="strong"><strong>By Convention</strong></span></p><p>The convention is such that if you have a contract under (for example)
<code class="literal">src/test/resources/contract/foo/bar/baz/</code> and set the value of the
<code class="literal">packageWithBaseClasses</code> property to <code class="literal">com.example.base</code>, then Spring Cloud Contract
Verifier assumes that there is a <code class="literal">BarBazBase</code> class under the <code class="literal">com.example.base</code> package.
In other words, the system takes the last two parts of the package, if they exist, and
forms a class with a <code class="literal">Base</code> suffix. This rule takes precedence over <span class="strong"><strong>baseClassForTests</strong></span>.
Here is an example of how it works in the <code class="literal">contracts</code> closure:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>hello<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p><span class="strong"><strong>By Mapping</strong></span></p><p>You can manually map a regular expression of the contract&#8217;s package to fully qualified
name of the base class for the matched contract. You have to provide a list called
<code class="literal">baseClassMappings</code> that consists <code class="literal">baseClassMapping</code> objects that takes a
<code class="literal">contractPackageRegex</code> to <code class="literal">baseClassFQN</code> mapping. Consider the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassForTests&gt;</span>com.example.FooBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassForTests&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractPackageRegex&gt;</span>.*com.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractPackageRegex&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassFQN&gt;</span>com.example.TestBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassFQN&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>Assume that you have contracts under these two locations:
* <code class="literal">src/test/resources/contract/com/</code>
* <code class="literal">src/test/resources/contract/foo/</code></p><p>By providing the <code class="literal">baseClassForTests</code>, we have a fallback in case mapping did not succeed.
(You can also provide the <code class="literal">packageWithBaseClasses</code> as a fallback.) That way, the tests
generated from <code class="literal">src/test/resources/contract/com/</code> contracts extend the
<code class="literal">com.example.ComBase</code>, whereas the rest of the tests extend <code class="literal">com.example.FooBase</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-invoking-generated-tests" href="#maven-invoking-generated-tests"></a>88.2.10&nbsp;Invoking generated tests</h3></div></div></div><p>The Spring Cloud Contract Maven Plugin generates verification code in a directory called
<code class="literal">/generated-test-sources/contractVerifier</code> and attaches this directory to <code class="literal">testCompile</code>
goal.</p><p>For Groovy Spock code, use the following:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.codehaus.gmavenplus<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>gmavenplus-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>1.5<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>testCompile<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSources&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.basedir}/src/test/groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.build.directory}/generated-test-sources/contractVerifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSources&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>To ensure that provider side is compliant with defined contracts, you need to invoke
<code class="literal">mvn generateTest test</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-pushing-stubs-to-scm" href="#maven-pushing-stubs-to-scm"></a>88.2.11&nbsp;Pushing stubs to SCM</h3></div></div></div><p>If you&#8217;re using the SCM repository to keep the contracts and
stubs, you might want to automate the step of pushing stubs to
the repository. To do that, it&#8217;s enough to add the <code class="literal">pushStubsToScm</code>
goal. Example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Base class mappings etc. --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- We want to pick contracts from a Git repository --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsRepositoryUrl&gt;</span>git://https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs-contracts-git.git<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsRepositoryUrl&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- We reuse the contract dependency section to set up the path
to the folder that contains the contract definitions. In our case the
path will be /groupId/artifactId/version/contracts --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>${project.groupId}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>${project.artifactId}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${project.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractDependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- The contracts mode can't be classpath --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractsMode&gt;</span>REMOTE<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractsMode&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;phase&gt;</span>package<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/phase&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- By default we will not push the stubs back to SCM,
you have to explicitly add it as a goal --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>pushStubsToScm<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p>Under <a class="xref" href="#scm-stub-downloader" title="94.6&nbsp;Using the SCM Stub Downloader">Section&nbsp;94.6, &#8220;Using the SCM Stub Downloader&#8221;</a> you can find all possible
configuration options that you can pass either via
the <code class="literal">&lt;configuration&gt;&lt;contractProperties&gt;</code> map, a system property
or an environment variable.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="maven-sts" href="#maven-sts"></a>88.2.12&nbsp;Maven Plugin and STS</h3></div></div></div><p>If you see the following exception while using STS:</p><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-contract/master/docs/src/main/asciidoc/images/sts_exception.png" alt="STS Exception"></div></div><p>When you click on the error marker you should see something like this:</p><pre class="programlisting"> plugin:<span class="hl-number">1.1</span>.<span class="hl-number">0.</span>M1:convert:default-convert:process-<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>-resources) org.apache.maven.plugin.PluginExecutionException: Execution default-convert of goal org.springframework.cloud:spring-
cloud-contract-maven-plugin:<span class="hl-number">1.1</span>.<span class="hl-number">0.</span>M1:convert failed. at org.apache.maven.plugin.DefaultBuildPluginManager.executeMojo(DefaultBuildPluginManager.java:<span class="hl-number">145</span>) at
org.eclipse.m2e.core.internal.embedder.MavenImpl.execute(MavenImpl.java:<span class="hl-number">331</span>) at org.eclipse.m2e.core.internal.embedder.MavenImpl$<span class="hl-number">11.</span>call(MavenImpl.java:<span class="hl-number">1362</span>) at
...
org.eclipse.core.internal.jobs.Worker.run(Worker.java:<span class="hl-number">55</span>) Caused by: java.lang.NullPointerException at
org.eclipse.m2e.core.internal.builder.plexusbuildapi.EclipseIncrementalBuildContext.hasDelta(EclipseIncrementalBuildContext.java:<span class="hl-number">53</span>) at
org.sonatype.plexus.build.incremental.ThreadBuildContext.hasDelta(ThreadBuildContext.java:<span class="hl-number">59</span>) at</pre><p>In order to fix this issue, provide the following section in your <code class="literal">pom.xml</code>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!--This plugin's configuration is used to store Eclipse m2e settings
only. It has no influence on the Maven build itself. --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.eclipse.m2e<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>lifecycle-mapping<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>1.0.0<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;lifecycleMappingMetadata&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginExecutions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginExecution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginExecutionFilter&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;versionRange&gt;</span>[1.0,)<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/versionRange&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>convert<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginExecutionFilter&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;action&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execute /&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/action&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginExecution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginExecutions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/lifecycleMappingMetadata&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_maven_plugin_with_spock_tests" href="#_maven_plugin_with_spock_tests"></a>88.2.13&nbsp;Maven Plugin with Spock Tests</h3></div></div></div><p>You can select the <a class="link" href="http://spockframework.org/" target="_top">Spock Framework</a> for creating and executing the auto-generated contract
verification tests with both Maven and Gradle plugin. However, whereas with Gradle its really straightforward,
in Maven you will require some additional setup in order to make the tests compile and execute properly.</p><p>First of all, you will have to use a plugin, such as <a class="link" href="https://github.com/groovy/GMavenPlus" target="_top">GMavenPlus</a> plugin,
to add Groovy to your project. In GMavenPlus plugin, you will need to explicitly set test sources, including both the
path where your base test classes are defined and the path were the generated contract tests are added.
Please refer to the example below:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.codehaus.gmavenplus<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>gmavenplus-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>1.6.1<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>compileTests<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>addTestSources<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSources&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.basedir}/src/test/groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>
${project.basedir}/target/generated-test-sources/contracts/com/example/beer
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.gvy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSource&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testSources&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.codehaus.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>groovy-all<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>2.4.15<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>runtime<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span></pre><p>If you uphold to the Spock convention of ending the test class names with <code class="literal">Spec</code>, you will also need to adjust your Maven
Surefire plugin setup, like in the following example:</p><pre class="programlisting"></pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stubs_and_transitive_dependencies" href="#_stubs_and_transitive_dependencies"></a>88.3&nbsp;Stubs and Transitive Dependencies</h2></div></div></div><p>The Maven and Gradle plugin that add the tasks that create the stubs jar for you. One
problem that arises is that, when reusing the stubs, you can mistakenly import all of
that stub&#8217;s dependencies. When building a Maven artifact, even though you have a couple
of different jars, all of them share one pom:</p><pre class="programlisting">&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-<span class="hl-number">20160903.075506</span>-<span class="hl-number">1</span>-stubs.jar
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-<span class="hl-number">20160903.075506</span>-<span class="hl-number">1</span>-stubs.jar.sha1
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-<span class="hl-number">20160903.075655</span>-<span class="hl-number">2</span>-stubs.jar
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-<span class="hl-number">20160903.075655</span>-<span class="hl-number">2</span>-stubs.jar.sha1
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-SNAPSHOT.jar
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-SNAPSHOT.pom
&#9500;&#9472;&#9472; github-webhook-<span class="hl-number">0.0</span>.<span class="hl-number">1.</span>BUILD-SNAPSHOT-stubs.jar
&#9500;&#9472;&#9472; ...
&#9492;&#9472;&#9472; ...</pre><p>There are three possibilities of working with those dependencies so as not to have any
issues with transitive dependencies:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Mark all application dependencies as optional</li><li class="listitem">Create a separate artifactid for the stubs</li><li class="listitem">Exclude dependencies on the consumer side</li></ul></div><p><span class="strong"><strong>Mark all application dependencies as optional</strong></span></p><p>If, in the <code class="literal">github-webhook</code> application, you mark all of your dependencies as optional,
when you include the <code class="literal">github-webhook</code> stubs in another application (or when that
dependency gets downloaded by Stub Runner) then, since all of the dependencies are
optional, they will not get downloaded.</p><p><span class="strong"><strong>Create a separate <code class="literal">artifactid</code> for the stubs</strong></span></p><p>If you create a separate <code class="literal">artifactid</code>, then you can set it up in whatever way you wish.
For example, you might decide to have no dependencies at all.</p><p><span class="strong"><strong>Exclude dependencies on the consumer side</strong></span></p><p>As a consumer, if you add the stub dependency to your classpath, you can explicitly
exclude the unwanted dependencies.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_scenarios" href="#_scenarios"></a>88.4&nbsp;Scenarios</h2></div></div></div><p>You can handle scenarios with Spring Cloud Contract Verifier. All you need to do is to
stick to the proper naming convention while creating your contracts. The convention
requires including an order number followed by an underscore. This will work regardles
of whether you&#8217;re working with YAML or Groovy. Example:</p><pre class="screen">my_contracts_dir\
scenario1\
1_login.groovy
2_showCart.groovy
3_logout.groovy</pre><p>Such a tree causes Spring Cloud Contract Verifier to generate WireMock&#8217;s scenario with a
name of <code class="literal">scenario1</code> and the three following steps:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">login marked as <code class="literal">Started</code> pointing to&#8230;&#8203;</li><li class="listitem">showCart marked as <code class="literal">Step1</code> pointing to&#8230;&#8203;</li><li class="listitem">logout marked as <code class="literal">Step2</code> which will close the scenario.</li></ol></div><p>More details about WireMock scenarios can be found at
<a class="link" href="http://wiremock.org/docs/stateful-behaviour/" target="_top">http://wiremock.org/docs/stateful-behaviour/</a></p><p>Spring Cloud Contract Verifier also generates tests with a guaranteed order of execution.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="docker-project" href="#docker-project"></a>88.5&nbsp;Docker Project</h2></div></div></div><p>We&#8217;re publishing a <code class="literal">springcloud/spring-cloud-contract</code> Docker image
that contains a project that will generate tests and execute them in <code class="literal">EXPLICIT</code> mode
against a running application.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">EXPLICIT</code> mode means that the tests generated from contracts will send
real requests and not the mocked ones.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_short_intro_to_maven_jars_and_binary_storage" href="#_short_intro_to_maven_jars_and_binary_storage"></a>88.5.1&nbsp;Short intro to Maven, JARs and Binary storage</h3></div></div></div><p>Since the Docker image can be used by non JVM projects, it&#8217;s good to
explain the basic terms behind Spring Cloud Contract packaging defaults.</p><p>Part of the following definitions were taken from the <a class="link" href="https://maven.apache.org/glossary.html" target="_top">Maven Glossary</a></p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">Project</code>: Maven thinks in terms of projects. Everything that you
will build are projects. Those projects follow a well defined
&#8220;Project Object Model&#8221;. Projects can depend on other projects,
in which case the latter are called &#8220;dependencies&#8221;. A project may
consistent of several subprojects, however these subprojects are still
treated equally as projects.</li><li class="listitem"><code class="literal">Artifact</code>: An artifact is something that is either produced or used
by a project. Examples of artifacts produced by Maven for a project
include: JARs, source and binary distributions. Each artifact
is uniquely identified by a group id and an artifact ID which is
unique within a group.</li><li class="listitem"><code class="literal">JAR</code>: JAR stands for Java ARchive. It&#8217;s a format based on
the ZIP file format. Spring Cloud Contract packages the contracts and generated
stubs in a JAR file.</li><li class="listitem"><code class="literal">GroupId</code>: A group ID is a universally unique identifier for a project.
While this is often just the project name (eg. commons-collections),
it is helpful to use a fully-qualified package name to distinguish it
from other projects with a similar name (eg. org.apache.maven).
Typically, when published to the Artifact Manager, the <code class="literal">GroupId</code> will get
slash separated and form part of the URL. E.g. for group id <code class="literal">com.example</code>
and artifact id <code class="literal">application</code> would be <code class="literal">/com/example/application/</code>.</li><li class="listitem"><code class="literal">Classifier</code>: The Maven dependency notation looks as follows:
<code class="literal">groupId:artifactId:version:classifier</code>. The classifier is additional suffix
passed to the dependency. E.g. <code class="literal">stubs</code>, <code class="literal">sources</code>. The same dependency
e.g. <code class="literal">com.example:application</code> can produce multiple artifacts that
differ from each other with the classifier.</li><li class="listitem"><code class="literal">Artifact manager</code>: When you generate binaries / sources / packages, you would
like them to be available for others to download / reference or reuse. In case
of the JVM world those artifacts would be JARs, for Ruby these are gems
and for Docker those would be Docker images. You can store those artifacts
in a manager. Examples of such managers can be <a class="link" href="https://jfrog.com/artifactory/" target="_top">Artifactory</a>
or <a class="link" href="http://www.sonatype.org/nexus/" target="_top">Nexus</a>.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_it_works_2" href="#_how_it_works_2"></a>88.5.2&nbsp;How it works</h3></div></div></div><p>The image searches for contracts under the <code class="literal">/contracts</code> folder.
The output from running the tests will be available under
<code class="literal">/spring-cloud-contract/build</code> folder (it&#8217;s useful for debugging
purposes).</p><p>It&#8217;s enough for you to mount your contracts, pass the environment variables
and the image will:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">generate the contract tests</li><li class="listitem">execute the tests against the provided URL</li><li class="listitem">generate the <a class="link" href="http://wiremock.org" target="_top">WireMock</a> stubs</li><li class="listitem">(optional - turned on by default) publish the stubs to a Artifact Manager</li></ul></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_environment_variables" href="#_environment_variables"></a>Environment Variables</h4></div></div></div><p>The Docker image requires some environment variables to point to
your running application, to the Artifact manager instance etc.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">PROJECT_GROUP</code> - your project&#8217;s group id. Defaults to <code class="literal">com.example</code></li><li class="listitem"><code class="literal">PROJECT_VERSION</code> - your project&#8217;s version. Defaults to <code class="literal">0.0.1-SNAPSHOT</code></li><li class="listitem"><code class="literal">PROJECT_NAME</code> - artifact id. Defaults to <code class="literal">example</code></li><li class="listitem"><code class="literal">REPO_WITH_BINARIES_URL</code> - URL of your Artifact Manager. Defaults to <code class="literal"><a class="link" href="http://localhost:8081/artifactory/libs-release-local" target="_top">http://localhost:8081/artifactory/libs-release-local</a></code>
which is the default URL of <a class="link" href="https://jfrog.com/artifactory/" target="_top">Artifactory</a> running locally</li><li class="listitem"><code class="literal">REPO_WITH_BINARIES_USERNAME</code> - (optional) username when the Artifact Manager is secured</li><li class="listitem"><code class="literal">REPO_WITH_BINARIES_PASSWORD</code> - (optional) password when the Artifact Manager is secured</li><li class="listitem"><code class="literal">PUBLISH_ARTIFACTS</code> - if set to <code class="literal">true</code> then will publish artifact to binary storage. Defaults to <code class="literal">true</code>.</li></ul></div><p>These environment variables are used when contracts lay in an external repository. To enable
this feature you must set the <code class="literal">EXTERNAL_CONTRACTS_ARTIFACT_ID</code> environment variable.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">EXTERNAL_CONTRACTS_GROUP_ID</code> - group id of the project with contracts. Defaults to <code class="literal">com.example</code></li><li class="listitem"><code class="literal">EXTERNAL_CONTRACTS_ARTIFACT_ID</code>- artifact id of the project with contracts.</li><li class="listitem"><code class="literal">EXTERNAL_CONTRACTS_CLASSIFIER</code>- classifier of the project with contracts. Empty by default</li><li class="listitem"><code class="literal">EXTERNAL_CONTRACTS_VERSION</code> - version of the project with contracts. Defaults to <code class="literal">+</code>, equivalent to picking the latest</li><li class="listitem"><code class="literal">EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL</code> - URL of your Artifact Manager. Defaults to value of <code class="literal">REPO_WITH_BINARIES_URL</code> env var.
If that&#8217;s not set, defaults to <code class="literal"><a class="link" href="http://localhost:8081/artifactory/libs-release-local" target="_top">http://localhost:8081/artifactory/libs-release-local</a></code>
which is the default URL of <a class="link" href="https://jfrog.com/artifactory/" target="_top">Artifactory</a> running locally</li><li class="listitem"><code class="literal">EXTERNAL_CONTRACTS_PATH</code> - path to contracts for the given project, inside the project with contracts.
Defaults to slash separated <code class="literal">EXTERNAL_CONTRACTS_GROUP_ID</code> concatenated with <code class="literal">/</code> and <code class="literal">EXTERNAL_CONTRACTS_ARTIFACT_ID</code>. E.g.
for group id <code class="literal">foo.bar</code> and artifact id <code class="literal">baz</code>, would result in <code class="literal">foo/bar/baz</code> contracts path.</li><li class="listitem"><code class="literal">EXTERNAL_CONTRACTS_WORK_OFFLINE</code> - if set to <code class="literal">true</code> then will retrieve artifact with contracts
from the container&#8217;s <code class="literal">.m2</code>. Mount your local <code class="literal">.m2</code> as a volume available at the container&#8217;s <code class="literal">/root/.m2</code> path.
You must not set both <code class="literal">EXTERNAL_CONTRACTS_WORK_OFFLINE</code> and <code class="literal">EXTERNAL_CONTRACTS_REPO_WITH_BINARIES_URL</code>.</li></ul></div><p>These environment variables are used when tests are executed:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">APPLICATION_BASE_URL</code> - url against which tests should be executed.
Remember that it has to be accessible from the Docker container (e.g. <code class="literal">localhost</code>
will not work)</li><li class="listitem"><code class="literal">APPLICATION_USERNAME</code> - (optional) username for basic authentication to your application</li><li class="listitem"><code class="literal">APPLICATION_PASSWORD</code> - (optional) password for basic authentication to your application</li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_example_of_usage" href="#_example_of_usage"></a>88.5.3&nbsp;Example of usage</h3></div></div></div><p>Let&#8217;s take a look at a simple MVC application</p><pre class="programlisting">$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">cd</span> bookstore</pre><p>The contracts are available under <code class="literal">/contracts</code> folder.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="docker-server-side" href="#docker-server-side"></a>88.5.4&nbsp;Server side (nodejs)</h3></div></div></div><p>Since we want to run tests, we could just execute:</p><pre class="programlisting">$ npm <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span></pre><p>however, for learning purposes, let&#8217;s split it into pieces:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Stop docker infra (nodejs, artifactory)</span>
$ ./stop_infra.sh
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Start docker infra (nodejs, artifactory)</span>
$ ./setup_infra.sh
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Kill &amp; Run app</span>
$ pkill -f <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"node app"</span>
$ nohup node app &amp;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Prepare environment variables</span>
$ SC_CONTRACT_DOCKER_VERSION=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"..."</span>
$ APP_IP=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"192.168.0.100"</span>
$ APP_PORT=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"3000"</span>
$ ARTIFACTORY_PORT=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"8081"</span>
$ APPLICATION_BASE_URL=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://${APP_IP}:${APP_PORT}"</span>
$ ARTIFACTORY_URL=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://${APP_IP}:${ARTIFACTORY_PORT}/artifactory/libs-release-local"</span>
$ CURRENT_DIR=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$( pwd )"</span>
$ CURRENT_FOLDER_NAME=${PWD<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">##*/}</span>
$ PROJECT_VERSION=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"0.0.1.RELEASE"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Execute contract tests</span>
$ docker run --rm -e <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"APPLICATION_BASE_URL=${APPLICATION_BASE_URL}"</span> -e <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PUBLISH_ARTIFACTS=true"</span> -e <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PROJECT_NAME=${CURRENT_FOLDER_NAME}"</span> -e <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"REPO_WITH_BINARIES_URL=${ARTIFACTORY_URL}"</span> -e <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PROJECT_VERSION=${PROJECT_VERSION}"</span> -v <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${CURRENT_DIR}/contracts/:/contracts:ro"</span> -v <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${CURRENT_DIR}/node_modules/spring-cloud-contract/output:/spring-cloud-contract-output/"</span> springcloud/spring-cloud-contract:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${SC_CONTRACT_DOCKER_VERSION}"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Kill app</span>
$ pkill -f <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"node app"</span></pre><p>What will happen is that via bash scripts:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">infrastructure will be set up (MongoDb, Artifactory).
In real life scenario you would just run the NodeJS application
with mocked database. In this example we want to show how we can
benefit from Spring Cloud Contract in no time.</li><li class="listitem"><p class="simpara">due to those constraints the contracts also represent the
stateful situation</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">first request is a <code class="literal">POST</code> that causes data to get inserted to the database</li><li class="listitem">second request is a <code class="literal">GET</code> that returns a list of data with 1 previously inserted element</li></ul></div></li><li class="listitem">the NodeJS application will be started (on port <code class="literal">3000</code>)</li><li class="listitem"><p class="simpara">contract tests will be generated via Docker and tests
will be executed against the running application</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">the contracts will be taken from <code class="literal">/contracts</code> folder.</li><li class="listitem">the output of the test execution is available under
<code class="literal">node_modules/spring-cloud-contract/output</code>.</li></ul></div></li><li class="listitem">the stubs will be uploaded to Artifactory. You can check them out
under <a class="link" href="http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/" target="_top">http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/</a> .
The stubs will be here <a class="link" href="http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar" target="_top">http://localhost:8081/artifactory/libs-release-local/com/example/bookstore/0.0.1.RELEASE/bookstore-0.0.1.RELEASE-stubs.jar</a>.</li></ul></div><p>To see how the client side looks like check out the <a class="xref" href="#stubrunner-docker" title="90.9&nbsp;Stub Runner Docker">Section&nbsp;90.9, &#8220;Stub Runner Docker&#8221;</a> section.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_contract_verifier_messaging" href="#_spring_cloud_contract_verifier_messaging"></a>89.&nbsp;Spring Cloud Contract Verifier Messaging</h2></div></div></div><p>Spring Cloud Contract Verifier lets you verify applications that use messaging as a
means of communication. All of the integrations shown in this document work with Spring,
but you can also create one of your own and use that.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_integrations_2" href="#_integrations_2"></a>89.1&nbsp;Integrations</h2></div></div></div><p>You can use one of the following four integration configurations:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Apache Camel</li><li class="listitem">Spring Integration</li><li class="listitem">Spring Cloud Stream</li><li class="listitem">Spring AMQP</li></ul></div><p>Since we use Spring Boot, if you have added one of these libraries to the classpath, all
the messaging configuration is automatically set up.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Remember to put <code class="literal">@AutoConfigureMessageVerifier</code> on the base class of your
generated tests. Otherwise, messaging part of Spring Cloud Contract Verifier does not
work.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you want to use Spring Cloud Stream, remember to add a dependency on
<code class="literal">org.springframework.cloud:spring-cloud-stream-test-support</code>, as shown here:</p></td></tr></table></div><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-test-support<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-stream-test-support"</span></pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_manual_integration_testing" href="#_manual_integration_testing"></a>89.2&nbsp;Manual Integration Testing</h2></div></div></div><p>The main interface used by the tests is
<code class="literal">org.springframework.cloud.contract.verifier.messaging.MessageVerifier</code>.
It defines how to send and receive messages. You can create your own implementation to
achieve the same goal.</p><p>In a test, you can inject a <code class="literal">ContractVerifierMessageExchange</code> to send and receive
messages that follow the contract. Then add <code class="literal">@AutoConfigureMessageVerifier</code> to your test.
Here&#8217;s an example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringTestRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureMessageVerifier</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MessagingContractTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MessageVerifier verifier;
...
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If your tests require stubs as well, then <code class="literal">@AutoConfigureStubRunner</code> includes the
messaging configuration, so you only need the one annotation.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_publisher_side_test_generation" href="#_publisher_side_test_generation"></a>89.3&nbsp;Publisher-Side Test Generation</h2></div></div></div><p>Having the <code class="literal">input</code> or <code class="literal">outputMessage</code> sections in your DSL results in creation of tests
on the publisher&#8217;s side. By default, JUnit 4 tests are created. However, there is also a
possibility to create JUnit 5 or Spock tests.</p><p>There are 3 main scenarios that we should take into consideration:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Scenario 1: There is no input message that produces an output message. The output
message is triggered by a component inside the application (for example, scheduler).</li><li class="listitem">Scenario 2: The input message triggers an output message.</li><li class="listitem">Scenario 3: The input message is consumed and there is no output message.</li></ul></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The destination passed to <code class="literal">messageFrom</code> or <code class="literal">sentTo</code> can have different
meanings for different messaging implementations. For <span class="strong"><strong>Stream</strong></span> and <span class="strong"><strong>Integration</strong></span> it is
first resolved as a <code class="literal">destination</code> of a channel. Then, if there is no such <code class="literal">destination</code>
it is resolved as a channel name. For <span class="strong"><strong>Camel</strong></span>, that&#8217;s a certain component (for example,
<code class="literal">jms</code>).</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_scenario_1_no_input_message" href="#_scenario_1_no_input_message"></a>89.3.1&nbsp;Scenario 1: No Input Message</h3></div></div></div><p>For the given contract:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">def contractDsl = Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>)
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'activemq:output'</span>)
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
messagingContentType(applicationJson())
}
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">label: some_label
input:
triggeredBy: bookReturnedTriggered
outputMessage:
sentTo: activemq:output
body:
bookName: foo
headers:
BOOK-NAME: foo
contentType: application/json</pre><p>
</p><p>The following JUnit test is created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
bookReturnedTriggered();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
ContractVerifierMessage response = contractVerifierMessaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"activemq:output"</span>);
assertThat(response).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOK-NAME"</span>)).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOK-NAME"</span>).toString()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"contentType"</span>)).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"contentType"</span>).toString()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()));
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bookName"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre><p>And the following Spock test would be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> when:
bookReturnedTriggered()
then:
ContractVerifierMessage response = contractVerifierMessaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'activemq:output'</span>)
assert response != null
response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>)?.toString() == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'contentType'</span>)?.toString() == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>
and:
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bookName"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_scenario_2_output_triggered_by_input" href="#_scenario_2_output_triggered_by_input"></a>89.3.2&nbsp;Scenario 2: Output Triggered by Input</h3></div></div></div><p>For the given contract:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">def contractDsl = Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">label: some_label
input:
messageFrom: jms:input
messageBody:
bookName: 'foo'
messageHeaders:
sample: header
outputMessage:
sentTo: jms:output
body:
bookName: foo
headers:
BOOK-NAME: foo</pre><p>
</p><p>The following JUnit test is created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\\"</span>bookName\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>foo\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"}"</span>
, headers()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sample"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"header"</span>));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
contractVerifierMessaging.send(inputMessage, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"jms:input"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
ContractVerifierMessage response = contractVerifierMessaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"jms:output"</span>);
assertThat(response).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOK-NAME"</span>)).isNotNull();
assertThat(response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"BOOK-NAME"</span>).toString()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.getPayload()));
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bookName"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre><p>And the following Spock test would be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\
</span>given:
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{"bookName":"foo"}'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>,
[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>]
)
when:
contractVerifierMessaging.send(inputMessage, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>)
then:
ContractVerifierMessage response = contractVerifierMessaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>)
assert response !- null
response.getHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>)?.toString() == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
and:
DocumentContext parsedJson = JsonPath.parse(contractVerifierObjectMapper.writeValueAsString(response.payload))
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bookName"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_scenario_3_no_output_message" href="#_scenario_3_no_output_message"></a>89.3.3&nbsp;Scenario 3: No Output Message</h3></div></div></div><p>For the given contract:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">def contractDsl = Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:delete'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookWasDeleted()'</span>)
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">label: some_label
input:
messageFrom: jms:delete
messageBody:
bookName: 'foo'
messageHeaders:
sample: header
assertThat: bookWasDeleted()</pre><p>
</p><p>The following JUnit test is created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\\"</span>bookName\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>foo\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"}"</span>
, headers()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sample"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"header"</span>));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
contractVerifierMessaging.send(inputMessage, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"jms:delete"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
bookWasDeleted();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre><p>And the following Spock test would be created:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span>given:
ContractVerifierMessage inputMessage = contractVerifierMessaging.create(
\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'\'\'{"bookName":"foo"}\'\'\',
</span> [<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>]
)
when:
contractVerifierMessaging.send(inputMessage, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:delete'</span>)
then:
noExceptionThrown()
bookWasDeleted()
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_consumer_stub_generation" href="#_consumer_stub_generation"></a>89.4&nbsp;Consumer Stub Generation</h2></div></div></div><p>Unlike the HTTP part, in messaging, we need to publish the Groovy DSL inside the JAR with
a stub. Then it is parsed on the consumer side and proper stubbed routes are created.</p><p>For more information, see <a class="xref" href="#stub-runner-for-messaging" title="91.&nbsp;Stub Runner for Messaging">Chapter&nbsp;91, <i>Stub Runner for Messaging</i></a> section.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-stream-rabbit<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-stub-runner<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-test-support<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencyManagement&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-dependencies<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>Greenwich.BUILD-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;type&gt;</span>pom<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/type&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>import<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencyManagement&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">ext {
contractsDir = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"mappings"</span>)
stubsOutputDirRoot = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/production/${project.name}-stubs/"</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Automatically added by plugin:</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// copyContracts - copies contracts to the output folder from which JAR will be created</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// verifierStubsJar - JAR with a provided stub suffix</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the presented publication is also added by the plugin but you can modify it as you wish</span>
publishing {
publications {
stubs(MavenPublication) {
artifactId <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.name}-stubs"</span>
artifact verifierStubsJar
}
}
}</pre><p class="secondary">
</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_contract_stub_runner" href="#_spring_cloud_contract_stub_runner"></a>90.&nbsp;Spring Cloud Contract Stub Runner</h2></div></div></div><p>One of the issues that you might encounter while using Spring Cloud Contract Verifier is
passing the generated WireMock JSON stubs from the server side to the client side (or to
various clients). The same takes place in terms of client-side generation for messaging.</p><p>Copying the JSON files and setting the client side for messaging manually is out of the
question. That is why we introduced Spring Cloud Contract Stub Runner. It can
automatically download and run the stubs for you.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_snapshot_versions" href="#_snapshot_versions"></a>90.1&nbsp;Snapshot versions</h2></div></div></div><p>Add the additional snapshot repository to your <code class="literal">build.gradle</code> file to use snapshot
versions, which are automatically uploaded after every successful build:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/repositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepositories&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Snapshots<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/snapshot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Milestones<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/milestone<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>spring-releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;name&gt;</span>Spring Releases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/name&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;url&gt;</span>https://repo.spring.io/release<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/url&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;enabled&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/enabled&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/snapshots&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepository&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pluginRepositories&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">buildscript {
repositories {
mavenCentral()
mavenLocal()
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/snapshot"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/milestone"</span> }
maven { url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://repo.spring.io/release"</span> }
}</pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="publishing-stubs-as-jars" href="#publishing-stubs-as-jars"></a>90.2&nbsp;Publishing Stubs as JARs</h2></div></div></div><p>The easiest approach would be to centralize the way stubs are kept. For example, you can
keep them as jars in a Maven repository.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>For both Maven and Gradle, the setup comes ready to work. However, you can customize
it if you want to.</p></td></tr></table></div><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- First disable the default jar setup in the properties section --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- we don't want the verifier to do a jar for us --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;spring.cloud.contract.verifier.skip&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/spring.cloud.contract.verifier.skip&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Next add the assembly plugin to your build --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- we want the assembly plugin to generate the JAR --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.apache.maven.plugins<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>maven-assembly-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>stub<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;phase&gt;</span>prepare-package<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/phase&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>single<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;inherited&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/inherited&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;attach&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/attach&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;descriptors&gt;</span>
$../../../../src/assembly/stub.xml
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/descriptors&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- Finally setup your assembly. Below you can find the contents of src/main/assembly/stub.xml --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;assembly</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;format&gt;</span>jar<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/format&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includeBaseDirectory&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includeBaseDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>src/main/java<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**com/example/model/*.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.build.directory}/classes<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**com/example/model/*.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.build.directory}/snippets/stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>META-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>$../../../../src/test/resources/contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>META-INF/${project.groupId}/${project.artifactId}/${project.version}/contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/assembly&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">ext {
contractsDir = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"mappings"</span>)
stubsOutputDirRoot = file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/production/${project.name}-stubs/"</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Automatically added by plugin:</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// copyContracts - copies contracts to the output folder from which JAR will be created</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// verifierStubsJar - JAR with a provided stub suffix</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the presented publication is also added by the plugin but you can modify it as you wish</span>
publishing {
publications {
stubs(MavenPublication) {
artifactId <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.name}-stubs"</span>
artifact verifierStubsJar
}
}
}</pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_core" href="#_stub_runner_core"></a>90.3&nbsp;Stub Runner Core</h2></div></div></div><p>Runs stubs for service collaborators. Treating stubs as contracts of services allows to use stub-runner as an implementation of
<a class="link" href="http://martinfowler.com/articles/consumerDrivenContracts.html" target="_top">Consumer Driven Contracts</a>.</p><p>Stub Runner allows you to automatically download the stubs of the provided dependencies (or pick those from the classpath), start WireMock servers for them and feed them with proper stub definitions.
For messaging, special stub routes are defined.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_retrieving_stubs" href="#_retrieving_stubs"></a>90.3.1&nbsp;Retrieving stubs</h3></div></div></div><p>You can pick the following options of acquiring stubs</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Aether based solution that downloads JARs with stubs from Artifactory / Nexus</li><li class="listitem">Classpath scanning solution that searches classpath via pattern to retrieve stubs</li><li class="listitem">Write your own implementation of the <code class="literal">org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder</code> for full customization</li></ul></div><p>The latter example is described in the <a class="link" href="#">Custom Stub Runner</a> section.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stub_downloading" href="#_stub_downloading"></a>Stub downloading</h4></div></div></div><p>You can control the stub downloading via the <code class="literal">stubsMode</code> switch. It picks value from the
<code class="literal">StubRunnerProperties.StubsMode</code> enum. You can use the following options</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">StubRunnerProperties.StubsMode.CLASSPATH</code> (default value) - will pick stubs from the classpath</li><li class="listitem"><code class="literal">StubRunnerProperties.StubsMode.LOCAL</code> - will pick stubs from a local storage (e.g. <code class="literal">.m2</code>)</li><li class="listitem"><code class="literal">StubRunnerProperties.StubsMode.REMOTE</code> - will pick stubs from a remote location</li></ul></div><p>Example:</p><pre class="programlisting">@AutoConfigureStubRunner(repositoryRoot=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://foo.bar"</span>, ids = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-api-producer:+:stubs:8095"</span>, stubsMode = StubRunnerProperties.StubsMode.LOCAL)</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_classpath_scanning" href="#_classpath_scanning"></a>Classpath scanning</h4></div></div></div><p>If you set the <code class="literal">stubsMode</code> property to <code class="literal">StubRunnerProperties.StubsMode.CLASSPATH</code>
(or set nothing since <code class="literal">CLASSPATH</code> is the default value) then classpath will get scanned.
Let&#8217;s look at the following example:</p><pre class="programlisting">@AutoConfigureStubRunner(ids = {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-api-producer:+:stubs:8095"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example.foo:bar:1.0.0:superstubs:8096"</span>
})</pre><p>If you&#8217;ve added the dependencies to your classpath</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>beer-api-producer-restdocs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;classifier&gt;</span>stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/classifier&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>0.0.1-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example.foo<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>bar<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;classifier&gt;</span>superstubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/classifier&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>1.0.0<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusion&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/exclusions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-api-producer-restdocs:0.0.1-SNAPSHOT:stubs"</span>) {
transitive = false
}
testCompile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example.foo:bar:1.0.0:superstubs"</span>) {
transitive = false
}</pre><p class="secondary">
</p><p>Then the following locations on your classpath will get scanned. For <code class="literal">com.example:beer-api-producer-restdocs</code></p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">/META-INF/com.example/beer-api-producer-restdocs/<span class="strong"><strong>*/</strong></span>.*</li><li class="listitem">/contracts/com.example/beer-api-producer-restdocs/<span class="strong"><strong>*/</strong></span>.*</li><li class="listitem">/mappings/com.example/beer-api-producer-restdocs/<span class="strong"><strong>*/</strong></span>.*</li></ul></div><p>and <code class="literal">com.example.foo:bar</code></p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">/META-INF/com.example.foo/bar/<span class="strong"><strong>*/</strong></span>.*</li><li class="listitem">/contracts/com.example.foo/bar/<span class="strong"><strong>*/</strong></span>.*</li><li class="listitem">/mappings/com.example.foo/bar/<span class="strong"><strong>*/</strong></span>.*</li></ul></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>As you can see you have to explicitly provide the group and artifact ids when packaging the
producer stubs.</p></td></tr></table></div><p>The producer would setup the contracts like this:</p><pre class="programlisting">&#9492;&#9472;&#9472; src
&#9492;&#9472;&#9472; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>
&#9492;&#9472;&#9472; resources
&#9492;&#9472;&#9472; contracts
&nbsp;&nbsp; &#9492;&#9472;&#9472; com.example
&nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; beer-api-producer-restdocs
&nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; nested
&nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; contract3.groovy</pre><p>To achieve proper stub packaging.</p><p>Or using the <a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/2.0.x/producer_with_restdocs/pom.xml" target="_top">Maven <code class="literal">assembly</code> plugin</a> or
<a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/blob/2.0.x/producer_with_restdocs/build.gradle" target="_top">Gradle Jar</a> task you have to create the following
structure in your stubs jar.</p><pre class="programlisting">&#9492;&#9472;&#9472; META-INF
&#9492;&#9472;&#9472; com.example
&#9492;&#9472;&#9472; beer-api-producer-restdocs
&#9492;&#9472;&#9472; <span class="hl-number">2.0</span>.<span class="hl-number">0</span>
&#9500;&#9472;&#9472; contracts
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; nested
&nbsp;&nbsp; &#9474; &#9492;&#9472;&#9472; contract2.groovy
&nbsp;&nbsp; &#9492;&#9472;&#9472; mappings
&nbsp;&nbsp; &#9492;&#9472;&#9472; mapping.json</pre><p>By maintaining this structure classpath gets scanned and you can profit from the messaging /
HTTP stubs without the need to download artifacts.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_running_stubs" href="#_running_stubs"></a>90.3.2&nbsp;Running stubs</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_running_using_main_app" href="#_running_using_main_app"></a>Running using main app</h4></div></div></div><p>You can set the following options to the main class:</p><pre class="programlisting">-c, --classifier Suffix <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> the jar containing stubs (e.
g. <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'stubs'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> the stub jar would
have a <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'stubs'</span> classifier <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> stubs:
foobar-stubs ). Defaults to <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'stubs'</span>
(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">default</span>: stubs)
--maxPort, --maxp &lt;Integer&gt; Maximum port value to be assigned to
the WireMock instance. Defaults to
<span class="hl-number">15000</span> (<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">default</span>: <span class="hl-number">15000</span>)
--minPort, --minp &lt;Integer&gt; Minimum port value to be assigned to
the WireMock instance. Defaults to
<span class="hl-number">10000</span> (<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">default</span>: <span class="hl-number">10000</span>)
-p, --password Password to user when connecting to
repository
--phost, --proxyHost Proxy host to use <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> repository
requests
--pport, --proxyPort [Integer] Proxy port to use <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> repository
requests
-r, --root Location of a Jar containing server
where you keep your stubs (e.g. http:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//nexus.</span>
net/content/repositories/repository)
-s, --stubs Comma separated list of Ivy
representation of jars with stubs.
Eg. groupid:artifactid1,groupid2:
artifactid2:classifier
--sm, --stubsMode Stubs mode to be used. Acceptable values
[CLASSPATH, LOCAL, REMOTE]
-u, --username Username to user when connecting to
repository</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_http_stubs" href="#_http_stubs"></a>HTTP Stubs</h4></div></div></div><p>Stubs are defined in JSON documents, whose syntax is defined in <a class="link" href="http://wiremock.org/stubbing.html" target="_top">WireMock documentation</a></p><p>Example:</p><pre class="programlisting">{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"GET"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/ping"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span class="hl-number">200</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"pong"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text/plain"</span>
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_viewing_registered_mappings" href="#_viewing_registered_mappings"></a>Viewing registered mappings</h4></div></div></div><p>Every stubbed collaborator exposes list of defined mappings under <code class="literal">__/admin/</code> endpoint.</p><p>You can also use the <code class="literal">mappingsOutputFolder</code> property to dump the mappings to files.
For annotation based approach it would look like this</p><pre class="programlisting">@AutoConfigureStubRunner(ids=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a.b.c:loanIssuance,a.b.c:fraudDetectionServer"</span>,
mappingsOutputFolder = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappings/"</span>)</pre><p>and for the JUnit approach like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ClassRule</span></em> <em><span class="hl-annotation" style="color: gray">@Shared</span></em> StubRunnerRule rule = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerRule()
.repoRoot(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://some_url"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a.b.c"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a.b.c:fraudDetectionServer"</span>)
.withMappingsOutputFolder(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappings"</span>)</pre><p>Then if you check out the folder <code class="literal">target/outputmappings</code> you would see the following structure</p><pre class="programlisting">.
&#9500;&#9472;&#9472; fraudDetectionServer_<span class="hl-number">13705</span>
&#9492;&#9472;&#9472; loanIssuance_<span class="hl-number">12255</span></pre><p>That means that there were two stubs registered. <code class="literal">fraudDetectionServer</code> was registered at port <code class="literal">13705</code>
and <code class="literal">loanIssuance</code> at port <code class="literal">12255</code>. If we take a look at one of the files we would see (for WireMock)
mappings available for the given server:</p><pre class="programlisting">[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"f9152eb9-bf77-4c38-8289-90be7d10d0d7"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/name"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"GET"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"uuid"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"f9152eb9-bf77-4c38-8289-90be7d10d0d7"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
...
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_messaging_stubs" href="#_messaging_stubs"></a>Messaging Stubs</h4></div></div></div><p>Depending on the provided Stub Runner dependency and the DSL the messaging routes are automatically set up.</p></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_junit_rule_and_stub_runner_junit5_extension" href="#_stub_runner_junit_rule_and_stub_runner_junit5_extension"></a>90.4&nbsp;Stub Runner JUnit Rule and Stub Runner JUnit5 Extension</h2></div></div></div><p>Stub Runner comes with a JUnit rule thanks to which you can very easily download and run stubs for given group and artifact id:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ClassRule</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> StubRunnerRule rule = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerRule()
.repoRoot(repoRoot())
.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>);</pre><p>There&#8217;s also a <code class="literal">StubRunnerExtension</code> available for JUnit 5. <code class="literal">StubRunnerRule</code> and <code class="literal">StubRunnerExtension</code> work in a very
similar fashion. After the rule/ extension is executed, Stub Runner connects to your Maven repository and for the given list of dependencies tries to:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">download them</li><li class="listitem">cache them locally</li><li class="listitem">unzip them to a temporary folder</li><li class="listitem">start a WireMock server for each Maven dependency on a random port from the provided range of ports / provided port</li><li class="listitem">feed the WireMock server with all JSON files that are valid WireMock definitions</li><li class="listitem">can also send messages (remember to pass an implementation of <code class="literal">MessageVerifier</code> interface)</li></ul></div><p>Stub Runner uses <a class="link" href="https://wiki.eclipse.org/Aether" target="_top">Eclipse Aether</a> mechanism to download the Maven dependencies.
Check their <a class="link" href="https://wiki.eclipse.org/Aether" target="_top">docs</a> for more information.</p><p>Since the <code class="literal">StubRunnerRule</code> and <code class="literal">StubRunnerExtension</code> implement the <code class="literal">StubFinder</code> they allow you to find the started stubs:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.stubrunner;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.net.URL;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.Collection;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.Map;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StubFinder <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> StubTrigger {
<strong class="hl-tag" style="color: blue">/**
* For the given groupId and artifactId tries to find the matching URL of the running
* stub.
* @param groupId - might be null. In that case a search only via artifactId takes
* place
* @return URL of a running stub or throws exception if not found
*/</strong>
URL findStubUrl(String groupId, String artifactId) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> StubNotFoundException;
<strong class="hl-tag" style="color: blue">/**
* For the given Ivy notation {@code [groupId]:artifactId:[version]:[classifier]}
* tries to find the matching URL of the running stub. You can also pass only
* {@code artifactId}.
* @param ivyNotation - Ivy representation of the Maven artifact
* @return URL of a running stub or throws exception if not found
*/</strong>
URL findStubUrl(String ivyNotation) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> StubNotFoundException;
<strong class="hl-tag" style="color: blue">/**
* Returns all running stubs
*/</strong>
RunningStubs findAllRunningStubs();
<strong class="hl-tag" style="color: blue">/**
* Returns the list of Contracts
*/</strong>
Map&lt;StubConfiguration, Collection&lt;Contract&gt;&gt; getContracts();
}</pre><p>Example of usage in Spock tests:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ClassRule</span></em> <em><span class="hl-annotation" style="color: gray">@Shared</span></em> StubRunnerRule rule = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerRule()
.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
.repoRoot(StubRunnerRuleSpec.getResource(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/m2repo/repository"</span>).toURI().toString())
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>)
.withMappingsOutputFolder(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappingsforrule"</span>)
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should start WireMock servers'</span>() {
expect: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'WireMocks are running'</span>
rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) != null
rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) != null
rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) == rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>)
rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer'</span>) != null
and:
rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>)
rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>)
rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer'</span>)
and: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Stubs were registered'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${rule.findStubUrl('loanIssuance').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${rule.findStubUrl('fraudDetectionServer').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should output mappings to output folder'</span>() {
when:
def url = rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>)
then:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> File(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappingsforrule"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer_${url.port}"</span>).exists()
}</pre><p>Example of usage in JUnit tests:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> should_start_wiremock_servers() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// expect: 'WireMocks are running'</span>
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isNotNull();
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isNotNull();
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isEqualTo(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>));
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>)).isNotNull();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
then(rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isTrue();
then(rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)).isTrue();
then(rule.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>)).isTrue();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and: 'Stubs were registered'</span>
then(httpGet(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>).toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/name"</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>);
then(httpGet(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>).toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/name"</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>);
}</pre><p>JUnit 5 Extension example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Visible for Junit</span>
<em><span class="hl-annotation" style="color: gray">@RegisterExtension</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> StubRunnerExtension stubRunnerExtension = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerExtension()
.repoRoot(repoRoot())
.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>)
.withMappingsOutputFolder(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappingsforrule"</span>);
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> should_start_WireMock_servers() {
assertThat(stubRunnerExtension.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isNotNull();
assertThat(stubRunnerExtension.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isNotNull();
assertThat(stubRunnerExtension.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isEqualTo(stubRunnerExtension
.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>));
assertThat(stubRunnerExtension
.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>)).isNotNull();
}</pre><p>Check the <span class="strong"><strong>Common properties for JUnit and Spring</strong></span> for more information on how to apply global configuration of Stub Runner.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>To use the JUnit rule or JUnit 5 extension together with messaging, you have to provide an implementation of the
<code class="literal">MessageVerifier</code> interface to the rule builder (e.g. <code class="literal">rule.messageVerifier(new MyMessageVerifier())</code>).
If you don&#8217;t do this, then whenever you try to send a message an exception will be thrown.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_maven_settings" href="#_maven_settings"></a>90.4.1&nbsp;Maven settings</h3></div></div></div><p>The stub downloader honors Maven settings for a different local repository folder.
Authentication details for repositories and profiles are currently not taken into account, so you need to specify it using the properties mentioned above.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_providing_fixed_ports" href="#_providing_fixed_ports"></a>90.4.2&nbsp;Providing fixed ports</h3></div></div></div><p>You can also run your stubs on fixed ports. You can do it in two different ways. One is to pass it in the properties, and the other via fluent API of
JUnit rule.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_fluent_api" href="#_fluent_api"></a>90.4.3&nbsp;Fluent API</h3></div></div></div><p>When using the <code class="literal">StubRunnerRule</code> or <code class="literal">StubRunnerExtension</code> you can add a stub to download and then pass the port for the last downloaded stub.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ClassRule</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> StubRunnerRule rule = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubRunnerRule()
.repoRoot(repoRoot())
.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)
.withPort(<span class="hl-number">12345</span>)
.downloadStub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer:12346"</span>);</pre><p>You can see that for this example the following test is valid:</p><pre class="programlisting">then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>)).isEqualTo(URI.create(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:12345"</span>).toURL());
then(rule.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)).isEqualTo(URI.create(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost:12346"</span>).toURL());</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stub_runner_with_spring" href="#_stub_runner_with_spring"></a>90.4.4&nbsp;Stub Runner with Spring</h3></div></div></div><p>Sets up Spring configuration of the Stub Runner project.</p><p>By providing a list of stubs inside your configuration file the Stub Runner automatically downloads
and registers in WireMock the selected stubs.</p><p>If you want to find the URL of your stubbed dependency you can autowire the <code class="literal">StubFinder</code> interface and use
its methods as presented below:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(properties = [" stubrunner.cloud.enabled=false",
'foo=${stubrunner.runningstubs.fraudDetectionServer.port}',
'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}'])</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/")</span></em>
<em><span class="hl-annotation" style="color: gray">@ActiveProfiles("test")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerConfigurationSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> StubFinder stubFinder
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Environment environment
<em><span class="hl-annotation" style="color: gray">@StubRunnerPort("fraudDetectionServer")</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> fraudDetectionServerPort
<em><span class="hl-annotation" style="color: gray">@StubRunnerPort("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer")</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> fraudDetectionServerPortWithGroupId
<em><span class="hl-annotation" style="color: gray">@Value('${foo}')</span></em> Integer foo
<em><span class="hl-annotation" style="color: gray">@BeforeClass</span></em>
<em><span class="hl-annotation" style="color: gray">@AfterClass</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setupProps() {
System.clearProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.repository.root"</span>)
System.clearProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.classifier"</span>)
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should start WireMock servers'</span>() {
expect: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'WireMocks are running'</span>
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) != null
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) != null
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) == stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>)
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>) == stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:loanIssuance'</span>)
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT'</span>) == stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs'</span>)
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer'</span>) != null
and:
stubFinder.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>)
stubFinder.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>)
stubFinder.findAllRunningStubs().isPresent(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer'</span>)
and: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Stubs were registered'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${stubFinder.findStubUrl('loanIssuance').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should throw an exception when stub is not found'</span>() {
when:
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'nonExistingService'</span>)
then:
thrown(StubNotFoundException)
when:
stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'nonExistingGroupId'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'nonExistingArtifactId'</span>)
then:
thrown(StubNotFoundException)
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should register started servers as environment variables'</span>() {
expect:
environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.loanIssuance.port"</span>) != null
stubFinder.findAllRunningStubs().getPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanIssuance"</span>) == (environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.loanIssuance.port"</span>) as Integer)
and:
environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.fraudDetectionServer.port"</span>) != null
stubFinder.findAllRunningStubs().getPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>) == (environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.fraudDetectionServer.port"</span>) as Integer)
and:
environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.fraudDetectionServer.port"</span>) != null
stubFinder.findAllRunningStubs().getPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>) == (environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port"</span>) as Integer)
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should be able to interpolate a running stub in the passed test property'</span>() {
given:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> fraudPort = stubFinder.findAllRunningStubs().getPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)
expect:
fraudPort &gt; <span class="hl-number">0</span>
environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>, Integer) == fraudPort
environment.getProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fooWithGroup"</span>, Integer) == fraudPort
foo == fraudPort
}
<em><span class="hl-annotation" style="color: gray">@Issue("#573")</span></em>
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should be able to retrieve the port of a running stub via an annotation'</span>() {
given:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> fraudPort = stubFinder.findAllRunningStubs().getPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)
expect:
fraudPort &gt; <span class="hl-number">0</span>
fraudDetectionServerPort == fraudPort
fraudDetectionServerPortWithGroupId == fraudPort
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should dump all mappings to a file'</span>() {
when:
def url = stubFinder.findStubUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer"</span>)
then:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> File(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/outputmappings/"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudDetectionServer_${url.port}"</span>).exists()
}
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {}
}</pre><p>for the following configuration file:</p><pre class="programlisting">stubrunner:
repositoryRoot: classpath:m2repo/repository/
ids:
- org.springframework.cloud.contract.verifier.stubs:loanIssuance
- org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer
- org.springframework.cloud.contract.verifier.stubs:bootService
stubs-mode: remote</pre><p>Instead of using the properties you can also use the properties inside the <code class="literal">@AutoConfigureStubRunner</code>.
Below you can find an example of achieving the same result by setting values on the annotation.</p><pre class="programlisting">@AutoConfigureStubRunner(
ids = [<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:loanIssuance"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:bootService"</span>],
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"classpath:m2repo/repository/"</span>)</pre><p>Stub Runner Spring registers environment variables in the following manner
for every registered WireMock server. Example for Stub Runner ids
<code class="literal">com.example:foo</code>, <code class="literal">com.example:bar</code>.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">stubrunner.runningstubs.foo.port</code></li><li class="listitem"><code class="literal">stubrunner.runningstubs.com.example.foo.port</code></li><li class="listitem"><code class="literal">stubrunner.runningstubs.bar.port</code></li><li class="listitem"><code class="literal">stubrunner.runningstubs.com.example.bar.port</code></li></ul></div><p>Which you can reference in your code.</p><p>You can also use the <code class="literal">@StubRunnerPort</code> annotation to inject the port of a running stub.
Value of the annotation can be the <code class="literal">groupid:artifactid</code> or just the <code class="literal">artifactid</code>. Example for Stub Runner ids
<code class="literal">com.example:foo</code>, <code class="literal">com.example:bar</code>.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@StubRunnerPort("foo")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> fooPort;
<em><span class="hl-annotation" style="color: gray">@StubRunnerPort("com.example:bar")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> barPort;</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_spring_cloud" href="#_stub_runner_spring_cloud"></a>90.5&nbsp;Stub Runner Spring Cloud</h2></div></div></div><p>Stub Runner can integrate with Spring Cloud.</p><p>For real life examples you can check the</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/2.0.x/producer" target="_top">producer app sample</a></li><li class="listitem"><a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/tree/2.0.x/consumer_with_discovery" target="_top">consumer app sample</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stubbing_service_discovery" href="#_stubbing_service_discovery"></a>90.5.1&nbsp;Stubbing Service Discovery</h3></div></div></div><p>The most important feature of <code class="literal">Stub Runner Spring Cloud</code> is the fact that it&#8217;s stubbing</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">DiscoveryClient</code></li><li class="listitem"><code class="literal">Ribbon</code> <code class="literal">ServerList</code></li></ul></div><p>that means that regardless of the fact whether you&#8217;re using Zookeeper, Consul, Eureka or anything else, you don&#8217;t need that in your tests.
We&#8217;re starting WireMock instances of your dependencies and we&#8217;re telling your application whenever you&#8217;re using <code class="literal">Feign</code>, load balanced <code class="literal">RestTemplate</code>
or <code class="literal">DiscoveryClient</code> directly, to call those stubbed servers instead of calling the real Service Discovery tool.</p><p>For example this test will pass</p><pre class="programlisting">def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should make service discovery work'</span>() {
expect: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'WireMocks are running'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${stubFinder.findStubUrl('loanIssuance').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name"</span>.toURL().text == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>
and: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Stubs can be reached via load service discovery'</span>
restTemplate.getForObject(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://loanIssuance/name'</span>, String) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loanIssuance'</span>
restTemplate.getForObject(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://someNameThatShouldMapFraudDetectionServer/name'</span>, String) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'fraudDetectionServer'</span>
}</pre><p>for the following configuration file</p><pre class="programlisting">stubrunner:
idsToServiceIds:
ivyNotation: someValueInsideYourCode
fraudDetectionServer: someNameThatShouldMapFraudDetectionServer</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_test_profiles_and_service_discovery" href="#_test_profiles_and_service_discovery"></a>Test profiles and service discovery</h4></div></div></div><p>In your integration tests you typically don&#8217;t want to call neither a discovery service (e.g. Eureka)
or Config Server. That&#8217;s why you create an additional test configuration in which you want to disable
these features.</p><p>Due to certain limitations of <a class="link" href="https://github.com/spring-cloud/spring-cloud-commons/issues/156" target="_top"><code class="literal">spring-cloud-commons</code></a> to achieve this you have disable these properties
via a static block like presented below (example for Eureka)</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Hack to work around https://github.com/spring-cloud/spring-cloud-commons/issues/156</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> {
System.setProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"eureka.client.enabled"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"false"</span>);
System.setProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"spring.cloud.config.failFast"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"false"</span>);
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_additional_configuration" href="#_additional_configuration"></a>90.5.2&nbsp;Additional Configuration</h3></div></div></div><p>You can match the artifactId of the stub with the name of your app by using the <code class="literal">stubrunner.idsToServiceIds:</code> map.
You can disable Stub Runner Ribbon support by providing: <code class="literal">stubrunner.cloud.ribbon.enabled</code> equal to <code class="literal">false</code>
You can disable Stub Runner support by providing: <code class="literal">stubrunner.cloud.enabled</code> equal to <code class="literal">false</code></p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>By default all service discovery will be stubbed. That means that regardless of the fact if you have
an existing <code class="literal">DiscoveryClient</code> its results will be ignored. However, if you want to reuse it, just set
<code class="literal">stubrunner.cloud.delegate.enabled</code> to <code class="literal">true</code> and then your existing <code class="literal">DiscoveryClient</code> results will be
merged with the stubbed ones.</p></td></tr></table></div><p>The default Maven configuration used by Stub Runner can be tweaked either
via the following system properties or environment variables</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">maven.repo.local</code> - path to the custom maven local repository location</li><li class="listitem"><code class="literal">org.apache.maven.user-settings</code> - path to custom maven user settings location</li><li class="listitem"><code class="literal">org.apache.maven.global-settings</code> - path to maven global settings location</li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_boot_application" href="#_stub_runner_boot_application"></a>90.6&nbsp;Stub Runner Boot Application</h2></div></div></div><p>Spring Cloud Contract Stub Runner Boot is a Spring Boot application that exposes REST endpoints to
trigger the messaging labels and to access started WireMock servers.</p><p>One of the use-cases is to run some smoke (end to end) tests on a deployed application.
You can check out the <a class="link" href="https://github.com/spring-cloud/spring-cloud-pipelines" target="_top">Spring Cloud Pipelines</a>
project for more information.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_to_use_it" href="#_how_to_use_it"></a>90.6.1&nbsp;How to use it?</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stub_runner_server" href="#_stub_runner_server"></a>Stub Runner Server</h4></div></div></div><p>Just add the</p><pre class="programlisting">compile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-starter-stub-runner"</span></pre><p>Annotate a class with <code class="literal">@EnableStubRunnerServer</code>, build a fat-jar and you&#8217;re ready to go!</p><p>For the properties check the <span class="strong"><strong>Stub Runner Spring</strong></span> section.</p></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stub_runner_server_fat_jar" href="#_stub_runner_server_fat_jar"></a>Stub Runner Server Fat Jar</h4></div></div></div><p>You can download a standalone JAR from Maven (e.g. for version 2.0.1.RELEASE), as follows:</p><pre class="programlisting">$ wget -O stub-runner.jar <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'https://search.maven.org/remotecontent?filepath=org/springframework/cloud/spring-cloud-contract-stub-runner-boot/2.0.1.RELEASE/spring-cloud-contract-stub-runner-boot-2.0.1.RELEASE.jar'</span>
$ java -jar stub-runner.jar --stubrunner.ids=... --stubrunner.repositoryRoot=...</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_spring_cloud_cli" href="#_spring_cloud_cli"></a>Spring Cloud CLI</h4></div></div></div><p>Starting from <code class="literal">1.4.0.RELEASE</code> version of the <a class="link" href="http://cloud.spring.io/spring-cloud-cli" target="_top">Spring Cloud CLI</a>
project you can start Stub Runner Boot by executing <code class="literal">spring cloud stubrunner</code>.</p><p>In order to pass the configuration just create a <code class="literal">stubrunner.yml</code> file in the current working directory
or a subdirectory called <code class="literal">config</code> or in <code class="literal">~/.spring-cloud</code>. The file could look like this
(example for running stubs installed locally)</p><p><b>stubrunner.yml.&nbsp;</b>
</p><pre class="programlisting">stubrunner:
stubsMode: LOCAL
ids:
- com.example:beer-api-producer:+:9876</pre><p>
</p><p>and then just call <code class="literal">spring cloud stubrunner</code> from your terminal window to start
the Stub Runner server. It will be available at port <code class="literal">8750</code>.</p></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_endpoints_2" href="#_endpoints_2"></a>90.6.2&nbsp;Endpoints</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_http_2" href="#_http_2"></a>HTTP</h4></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">GET <code class="literal">/stubs</code> - returns a list of all running stubs in <code class="literal">ivy:integer</code> notation</li><li class="listitem">GET <code class="literal">/stubs/{ivy}</code> - returns a port for the given <code class="literal">ivy</code> notation (when calling the endpoint <code class="literal">ivy</code> can also be <code class="literal">artifactId</code> only)</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_messaging_2" href="#_messaging_2"></a>Messaging</h4></div></div></div><p>For Messaging</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">GET <code class="literal">/triggers</code> - returns a list of all running labels in <code class="literal">ivy : [ label1, label2 &#8230;&#8203;]</code> notation</li><li class="listitem">POST <code class="literal">/triggers/{label}</code> - executes a trigger with <code class="literal">label</code></li><li class="listitem">POST <code class="literal">/triggers/{ivy}/{label}</code> - executes a trigger with <code class="literal">label</code> for the given <code class="literal">ivy</code> notation (when calling the endpoint <code class="literal">ivy</code> can also be <code class="literal">artifactId</code> only)</li></ul></div></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_example" href="#_example"></a>90.6.3&nbsp;Example</h3></div></div></div><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(classes = StubRunnerBoot, loader = SpringBootContextLoader)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(properties = "spring.cloud.zookeeper.enabled=false")</span></em>
<em><span class="hl-annotation" style="color: gray">@ActiveProfiles("test")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerBootSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> StubRunning stubRunning
def setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpStubsController(stubRunning),
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TriggerController(stubRunning))
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should return a list of running stub servers in "full ivy:port" notation'</span>() {
when:
String response = RestAssuredMockMvc.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/stubs'</span>).body.asString()
then:
def root = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> JsonSlurper().parseText(response)
root.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">instanceof</span> Integer
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should return a port on which a [#stubId] stub is running'</span>() {
when:
def response = RestAssuredMockMvc.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/stubs/${stubId}"</span>)
then:
response.statusCode == <span class="hl-number">200</span>
Integer.valueOf(response.body.asString()) &gt; <span class="hl-number">0</span>
where:
stubId &lt;&lt; [<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:+:stubs'</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:+'</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService'</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bootService'</span>]
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should return 404 when missing stub was called'</span>() {
when:
def response = RestAssuredMockMvc.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/stubs/a:b:c:d"</span>)
then:
response.statusCode == <span class="hl-number">404</span>
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should return a list of messaging labels that can be triggered when version and classifier are passed'</span>() {
when:
String response = RestAssuredMockMvc.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/triggers'</span>).body.asString()
then:
def root = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> JsonSlurper().parseText(response)
root.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs'</span>?.containsAll([<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"delete_book"</span>,<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"return_book_1"</span>,<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"return_book_2"</span>])
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should trigger a messaging label'</span>() {
given:
StubRunning stubRunning = Mock()
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpStubsController(stubRunning), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TriggerController(stubRunning))
when:
def response = RestAssuredMockMvc.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/triggers/delete_book"</span>)
then:
response.statusCode == <span class="hl-number">200</span>
and:
<span class="hl-number">1</span> * stubRunning.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'delete_book'</span>)
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should trigger a messaging label for a stub with [#stubId] ivy notation'</span>() {
given:
StubRunning stubRunning = Mock()
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpStubsController(stubRunning), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TriggerController(stubRunning))
when:
def response = RestAssuredMockMvc.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/triggers/$stubId/delete_book"</span>)
then:
response.statusCode == <span class="hl-number">200</span>
and:
<span class="hl-number">1</span> * stubRunning.trigger(stubId, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'delete_book'</span>)
where:
stubId &lt;&lt; [<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService:stubs'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:bootService'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bootService'</span>]
}
def <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'should throw exception when trigger is missing'</span>() {
when:
RestAssuredMockMvc.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/triggers/missing_label"</span>)
then:
Exception e = thrown(Exception)
e.message.contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Exception occurred while trying to return [missing_label] label."</span>)
e.message.contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Available labels are"</span>)
e.message.contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs=[]"</span>)
e.message.contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud.contract.verifier.stubs:bootService:0.0.1-SNAPSHOT:stubs="</span>)
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_stub_runner_boot_with_service_discovery" href="#_stub_runner_boot_with_service_discovery"></a>90.6.4&nbsp;Stub Runner Boot with Service Discovery</h3></div></div></div><p>One of the possibilities of using Stub Runner Boot is to use it as a feed of stubs for "smoke-tests". What does it mean?
Let&#8217;s assume that you don&#8217;t want to deploy 50 microservice to a test environment in order
to check if your application is working fine. You&#8217;ve already executed a suite of tests during the build process
but you would also like to ensure that the packaging of your application is fine. What you can do
is to deploy your application to an environment, start it and run a couple of tests on it to see if
it&#8217;s working fine. We can call those tests smoke-tests since their idea is to check only a handful
of testing scenarios.</p><p>The problem with this approach is such that if you&#8217;re doing microservices most likely you&#8217;re
using a service discovery tool. Stub Runner Boot allows you to solve this issue by starting the
required stubs and register them in a service discovery tool. Let&#8217;s take a look at an example of
such a setup with Eureka. Let&#8217;s assume that Eureka was already running.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableStubRunnerServer</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableEurekaClient</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerBootEurekaExample {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(StubRunnerBootEurekaExample.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre><p>As you can see we want to start a Stub Runner Boot server <code class="literal">@EnableStubRunnerServer</code>, enable Eureka client <code class="literal">@EnableEurekaClient</code>
and we want to have the stub runner feature turned on <code class="literal">@AutoConfigureStubRunner</code>.</p><p>Now let&#8217;s assume that we want to start this application so that the stubs get automatically registered.
We can do it by running the app <code class="literal">java -jar ${SYSTEM_PROPS} stub-runner-boot-eureka-example.jar</code> where
<code class="literal">${SYSTEM_PROPS}</code> would contain the following list of properties</p><pre class="programlisting">-Dstubrunner.repositoryRoot=http://repo.spring.io/snapshots (<span class="hl-number">1</span>)
-Dstubrunner.cloud.stubbed.discovery.enabled=false (<span class="hl-number">2</span>)
-Dstubrunner.ids=org.springframework.cloud.contract.verifier.stubs:loanIssuance,org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer,org.springframework.cloud.contract.verifier.stubs:bootService (<span class="hl-number">3</span>)
-Dstubrunner.idsToServiceIds.fraudDetectionServer=someNameThatShouldMapFraudDetectionServer (<span class="hl-number">4</span>)
(<span class="hl-number">1</span>) - we tell Stub Runner where all the stubs reside
(<span class="hl-number">2</span>) - we don<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'t want the default behaviour where the discovery service is stubbed. That'</span>s why the stub registration will be picked
(<span class="hl-number">3</span>) - we provide a list of stubs to download
(<span class="hl-number">4</span>) - we provide a list of artifactId to serviceId mapping</pre><p>That way your deployed application can send requests to started WireMock servers via the service
discovery. Most likely points 1-3 could be set by default in <code class="literal">application.yml</code> cause they are not
likely to change. That way you can provide only the list of stubs to download whenever you start
the Stub Runner Boot.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stubs_per_consumer" href="#_stubs_per_consumer"></a>90.7&nbsp;Stubs Per Consumer</h2></div></div></div><p>There are cases in which 2 consumers of the same endpoint want to have 2 different responses.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>This approach also allows you to immediately know which consumer is using which part of your API.
You can remove part of a response that your API produces and you can see which of your autogenerated tests
fails. If none fails then you can safely delete that part of the response cause nobody is using it.</p></td></tr></table></div><p>Let&#8217;s look at the following example for contract defined for the producer called <code class="literal">producer</code>.
There are 2 consumers: <code class="literal">foo-consumer</code> and <code class="literal">bar-consumer</code>.</p><p><span class="strong"><strong>Consumer <code class="literal">foo-service</code></strong></span></p><pre class="programlisting">request {
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/foo'</span>
method GET()
}
response {
status OK()
body(
foo: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>
}
}</pre><p><span class="strong"><strong>Consumer <code class="literal">bar-service</code></strong></span></p><pre class="programlisting">request {
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/foo'</span>
method GET()
}
response {
status OK()
body(
bar: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>
}
}</pre><p>You can&#8217;t produce for the same request 2 different responses. That&#8217;s why you can properly package the
contracts and then profit from the <code class="literal">stubsPerConsumer</code> feature.</p><p>On the producer side the consumers can have a folder that contains contracts related only to them.
By setting the <code class="literal">stubrunner.stubs-per-consumer</code> flag to <code class="literal">true</code> we no longer register all stubs but only those that
correspond to the consumer application&#8217;s name. In other words we&#8217;ll scan the path of every stub and
if it contains the subfolder with name of the consumer in the path only then will it get registered.</p><p>On the <code class="literal">foo</code> producer side the contracts would look like this</p><pre class="programlisting">.
&#9492;&#9472;&#9472; contracts
&#9500;&#9472;&#9472; bar-consumer
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookReturnedForBar.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; shouldCallBar.groovy
&#9492;&#9472;&#9472; foo-consumer
&#9500;&#9472;&#9472; bookReturnedForFoo.groovy
&#9492;&#9472;&#9472; shouldCallFoo.groovy</pre><p>Being the <code class="literal">bar-consumer</code> consumer you can either set the <code class="literal">spring.application.name</code> or the <code class="literal">stubrunner.consumer-name</code> to <code class="literal">bar-consumer</code>
Or set the test as follows:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(properties = ["spring.application.name=bar-consumer"])</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
repositoryRoot = "classpath:m2repo/repository/",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerStubsPerConsumerSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
...
}</pre><p>Then only the stubs registered under a path that contains the <code class="literal">bar-consumer</code> in its name (i.e. those from the
<code class="literal">src/test/resources/contracts/bar-consumer/some/contracts/&#8230;&#8203;</code> folder) will be allowed to be referenced.</p><p>Or set the consumer name explicitly</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@ContextConfiguration(classes = Config, loader = SpringBootContextLoader)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureStubRunner(ids = "org.springframework.cloud.contract.verifier.stubs:producerWithMultipleConsumers",
repositoryRoot = "classpath:m2repo/repository/",
consumerName = "foo-consumer",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> StubRunnerStubsPerConsumerWithConsumerNameSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
...
}</pre><p>Then only the stubs registered under a path that contains the <code class="literal">foo-consumer</code> in its name (i.e. those from the
<code class="literal">src/test/resources/contracts/foo-consumer/some/contracts/&#8230;&#8203;</code> folder) will be allowed to be referenced.</p><p>You can check out <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/issues/224" target="_top">issue 224</a> for more
information about the reasons behind this change.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_common" href="#_common"></a>90.8&nbsp;Common</h2></div></div></div><p>This section briefly describes common properties, including:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#common-properties-junit-spring" title="90.8.1&nbsp;Common Properties for JUnit and Spring">Section&nbsp;90.8.1, &#8220;Common Properties for JUnit and Spring&#8221;</a></li><li class="listitem"><a class="xref" href="#stub-runner-stub-ids" title="90.8.2&nbsp;Stub Runner Stubs IDs">Section&nbsp;90.8.2, &#8220;Stub Runner Stubs IDs&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="common-properties-junit-spring" href="#common-properties-junit-spring"></a>90.8.1&nbsp;Common Properties for JUnit and Spring</h3></div></div></div><p>You can set repetitive properties by using system properties or Spring configuration
properties. Here are their names with their default values:</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Property name</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Default value</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Description</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.minPort</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Minimum value of a port for a started WireMock with stubs.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.maxPort</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>15000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum value of a port for a started WireMock with stubs.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.repositoryRoot</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maven repo URL. If blank, then call the local maven repo.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.classifier</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubs</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default classifier for the stub artifacts.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.stubsMode</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>CLASSPATH</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The way you want to fetch and register the stubs</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.ids</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Array of Ivy notation stubs to download.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.username</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Optional username to access the tool that stores the JARs with
stubs.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Optional password to access the tool that stores the JARs with
stubs.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.stubsPerConsumer</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Set to <code class="literal">true</code> if you want to use different stubs for
each consumer instead of registering all stubs for every consumer.</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.consumerName</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="" align="left" valign="top"><p>If you want to use a stub for each consumer and want to
override the consumer name just change this value.</p></td></tr></tbody></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="stub-runner-stub-ids" href="#stub-runner-stub-ids"></a>90.8.2&nbsp;Stub Runner Stubs IDs</h3></div></div></div><p>You can provide the stubs to download via the <code class="literal">stubrunner.ids</code> system property. They
follow this pattern:</p><pre class="programlisting">groupId:artifactId:version:classifier:port</pre><p>Note that <code class="literal">version</code>, <code class="literal">classifier</code> and <code class="literal">port</code> are optional.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">If you do not provide the <code class="literal">port</code>, a random one will be picked.</li><li class="listitem">If you do not provide the <code class="literal">classifier</code>, the default is used. (Note that you can
pass an empty classifier this way: <code class="literal">groupId:artifactId:version:</code>).</li><li class="listitem">If you do not provide the <code class="literal">version</code>, then the <code class="literal">+</code> will be passed and the latest one is
downloaded.</li></ul></div><p><code class="literal">port</code> means the port of the WireMock server.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting with version 1.0.4, you can provide a range of versions that you
would like the Stub Runner to take into consideration. You can read more about the
<a class="link" href="https://wiki.eclipse.org/Aether/New_and_Noteworthy#Version_Ranges" target="_top">Aether versioning
ranges here</a>.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="stubrunner-docker" href="#stubrunner-docker"></a>90.9&nbsp;Stub Runner Docker</h2></div></div></div><p>We&#8217;re publishing a <code class="literal">spring-cloud/spring-cloud-contract-stub-runner</code> Docker image
that will start the standalone version of Stub Runner.</p><p>If you want to learn more about the basics of Maven, artifact ids,
group ids, classifiers and Artifact Managers, just click here <a class="xref" href="#docker-project" title="88.5&nbsp;Docker Project">Section&nbsp;88.5, &#8220;Docker Project&#8221;</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_how_to_use_it_2" href="#_how_to_use_it_2"></a>90.9.1&nbsp;How to use it</h3></div></div></div><p>Just execute the docker image. You can pass any of the <a class="xref" href="#common-properties-junit-spring" title="90.8.1&nbsp;Common Properties for JUnit and Spring">Section&nbsp;90.8.1, &#8220;Common Properties for JUnit and Spring&#8221;</a>
as environment variables. The convention is that all the
letters should be upper case. The camel case notation should
and the dot (<code class="literal">.</code>) should be separated via underscore (<code class="literal">_</code>). E.g.
the <code class="literal">stubrunner.repositoryRoot</code> property should be represented
as a <code class="literal">STUBRUNNER_REPOSITORY_ROOT</code> environment variable.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_example_of_client_side_usage_in_a_non_jvm_project" href="#_example_of_client_side_usage_in_a_non_jvm_project"></a>90.9.2&nbsp;Example of client side usage in a non JVM project</h3></div></div></div><p>We&#8217;d like to use the stubs created in this <a class="xref" href="#docker-server-side" title="88.5.4&nbsp;Server side (nodejs)">Section&nbsp;88.5.4, &#8220;Server side (nodejs)&#8221;</a> step.
Let&#8217;s assume that we want to run the stubs on port <code class="literal">9876</code>. The NodeJS code
is available here:</p><pre class="programlisting">$ git clone https://github.com/spring-cloud-samples/spring-cloud-contract-nodejs
$ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">cd</span> bookstore</pre><p>Let&#8217;s run the Stub Runner Boot application with the stubs.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Provide the Spring Cloud Contract Docker version</span>
$ SC_CONTRACT_DOCKER_VERSION=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"..."</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># The IP at which the app is running and Docker container can reach it</span>
$ APP_IP=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"192.168.0.100"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Spring Cloud Contract Stub Runner properties</span>
$ STUBRUNNER_PORT=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"8083"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Stub coordinates 'groupId:artifactId:version:classifier:port'</span>
$ STUBRUNNER_IDS=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:bookstore:0.0.1.RELEASE:stubs:9876"</span>
$ STUBRUNNER_REPOSITORY_ROOT=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://${APP_IP}:8081/artifactory/libs-release-local"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Run the docker with Stub Runner Boot</span>
$ docker run --rm -e <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"STUBRUNNER_IDS=${STUBRUNNER_IDS}"</span> -e <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"STUBRUNNER_REPOSITORY_ROOT=${STUBRUNNER_REPOSITORY_ROOT}"</span> -e <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"STUBRUNNER_STUBS_MODE=REMOTE"</span> -p <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${STUBRUNNER_PORT}:${STUBRUNNER_PORT}"</span> -p <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"9876:9876"</span> springcloud/spring-cloud-contract-stub-runner:<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${SC_CONTRACT_DOCKER_VERSION}"</span></pre><p>What&#8217;s happening is that</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">a standalone Stub Runner application got started</li><li class="listitem">it downloaded the stub with coordinates <code class="literal">com.example:bookstore:0.0.1.RELEASE:stubs</code> on port <code class="literal">9876</code></li><li class="listitem">it got downloaded from Artifactory running at <code class="literal"><a class="link" href="http://192.168.0.100:8081/artifactory/libs-release-local" target="_top">http://192.168.0.100:8081/artifactory/libs-release-local</a></code></li><li class="listitem">after a while Stub Runner will be running on port <code class="literal">8083</code></li><li class="listitem">and the stubs will be running at port <code class="literal">9876</code></li></ul></div><p>On the server side we built a stateful stub. Let&#8217;s use curl to assert
that the stubs are setup properly.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># let's execute the first request (no response is returned)</span>
$ curl -H <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type:application/json"</span> -X POST --data <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "title" : "Title", "genre" : "Genre", "description" : "Description", "author" : "Author", "publisher" : "Publisher", "pages" : 100, "image_url" : "https://d213dhlpdb53mu.cloudfront.net/assets/pivotal-square-logo-41418bd391196c3022f3cd9f3959b3f6d7764c47873d858583384e759c7db435.svg", "buy_url" : "https://pivotal.io" }'</span> http://localhost:<span class="hl-number">9876</span>/api/books
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Now time for the second request</span>
$ curl -X GET http://localhost:<span class="hl-number">9876</span>/api/books
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># You will receive contents of the JSON</span></pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you want use the stubs that you have built locally, on your host,
then you should pass the environment variable <code class="literal">-e STUBRUNNER_STUBS_MODE=LOCAL</code> and mount
the volume of your local m2 <code class="literal">-v "${HOME}/.m2/:/root/.m2:ro"</code></p></td></tr></table></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="stub-runner-for-messaging" href="#stub-runner-for-messaging"></a>91.&nbsp;Stub Runner for Messaging</h2></div></div></div><p>Stub Runner can run the published stubs in memory. It can integrate with the following
frameworks:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Spring Integration</li><li class="listitem">Spring Cloud Stream</li><li class="listitem">Apache Camel</li><li class="listitem">Spring AMQP</li></ul></div><p>It also provides entry points to integrate with any other solution on the market.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you have multiple frameworks on the classpath Stub Runner will need to
define which one should be used. Let&#8217;s assume that you have both AMQP, Spring Cloud Stream and Spring Integration
on the classpath. Then you need to set <code class="literal">stubrunner.stream.enabled=false</code> and <code class="literal">stubrunner.integration.enabled=false</code>.
That way the only remaining framework is Spring AMQP.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_triggering" href="#_stub_triggering"></a>91.1&nbsp;Stub triggering</h2></div></div></div><p>To trigger a message, use the <code class="literal">StubTrigger</code> interface:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.stubrunner;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.Collection;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.Map;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StubTrigger {
<strong class="hl-tag" style="color: blue">/**
* Triggers an event by a given label for a given {@code groupid:artifactid} notation.
* You can use only {@code artifactId} too.
*
* Feature related to messaging.
* @return true - if managed to run a trigger
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> trigger(String ivyNotation, String labelName);
<strong class="hl-tag" style="color: blue">/**
* Triggers an event by a given label.
*
* Feature related to messaging.
* @return true - if managed to run a trigger
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> trigger(String labelName);
<strong class="hl-tag" style="color: blue">/**
* Triggers all possible events.
*
* Feature related to messaging.
* @return true - if managed to run a trigger
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> trigger();
<strong class="hl-tag" style="color: blue">/**
* Returns a mapping of ivy notation of a dependency to all the labels it has.
*
* Feature related to messaging.
*/</strong>
Map&lt;String, Collection&lt;String&gt;&gt; labels();
}</pre><p>For convenience, the <code class="literal">StubFinder</code> interface extends <code class="literal">StubTrigger</code>, so you only need one
or the other in your tests.</p><p><code class="literal">StubTrigger</code> gives you the following options to trigger a message:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#trigger-label" title="91.1.1&nbsp;Trigger by Label">Section&nbsp;91.1.1, &#8220;Trigger by Label&#8221;</a></li><li class="listitem"><a class="xref" href="#trigger-group-artifact-ids" title="91.1.2&nbsp;Trigger by Group and Artifact Ids">Section&nbsp;91.1.2, &#8220;Trigger by Group and Artifact Ids&#8221;</a></li><li class="listitem"><a class="xref" href="#trigger-artifact-ids" title="91.1.3&nbsp;Trigger by Artifact Ids">Section&nbsp;91.1.3, &#8220;Trigger by Artifact Ids&#8221;</a></li><li class="listitem"><a class="xref" href="#trigger-all-messages" title="91.1.4&nbsp;Trigger All Messages">Section&nbsp;91.1.4, &#8220;Trigger All Messages&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="trigger-label" href="#trigger-label"></a>91.1.1&nbsp;Trigger by Label</h3></div></div></div><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="trigger-group-artifact-ids" href="#trigger-group-artifact-ids"></a>91.1.2&nbsp;Trigger by Group and Artifact Ids</h3></div></div></div><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.verifier.stubs:streamService'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="trigger-artifact-ids" href="#trigger-artifact-ids"></a>91.1.3&nbsp;Trigger by Artifact Ids</h3></div></div></div><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'streamService'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="trigger-all-messages" href="#trigger-all-messages"></a>91.1.4&nbsp;Trigger All Messages</h3></div></div></div><pre class="programlisting">stubFinder.trigger()</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_camel" href="#_stub_runner_camel"></a>91.2&nbsp;Stub Runner Camel</h2></div></div></div><p>Spring Cloud Contract Verifier Stub Runner&#8217;s messaging module gives you an easy way to integrate with Apache Camel.
For the provided artifacts it will automatically download the stubs and register the required
routes.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_it_to_the_project" href="#_adding_it_to_the_project"></a>91.2.1&nbsp;Adding it to the project</h3></div></div></div><p>It&#8217;s enough to have both Apache Camel and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_disabling_the_functionality" href="#_disabling_the_functionality"></a>91.2.2&nbsp;Disabling the functionality</h3></div></div></div><p>If you need to disable this functionality just pass <code class="literal">stubrunner.camel.enabled=false</code> property.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_examples" href="#_examples"></a>91.2.3&nbsp;Examples</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_stubs_structure" href="#_stubs_structure"></a>Stubs structure</h4></div></div></div><p>Let us assume that we have the following Maven repository with a deployed stubs for the
<code class="literal">camelService</code> application.</p><pre class="programlisting">&#9492;&#9472;&#9472; .m2
&#9492;&#9472;&#9472; repository
&#9492;&#9472;&#9472; io
&#9492;&#9472;&#9472; codearte
&#9492;&#9472;&#9472; accurest
&#9492;&#9472;&#9472; stubs
&#9492;&#9472;&#9472; camelService
&#9500;&#9472;&#9472; <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; camelService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; camelService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; maven-metadata-local.xml
&#9492;&#9472;&#9472; maven-metadata-local.xml</pre><p>And the stubs contain the following structure:</p><pre class="programlisting">&#9500;&#9472;&#9472; META-INF
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; MANIFEST.MF
&#9492;&#9472;&#9472; repository
&#9500;&#9472;&#9472; accurest
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookDeleted.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookReturned1.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; bookReturned2.groovy
&#9492;&#9472;&#9472; mappings</pre><p>Let&#8217;s consider the following contracts (let' number it with <span class="strong"><strong>1</strong></span>):</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>
input {
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>)
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>)
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>and number <span class="strong"><strong>2</strong></span></p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_2'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_1_no_input_message_2" href="#_scenario_1_no_input_message_2"></a>Scenario 1 (no input message)</h4></div></div></div><p>So as to trigger a message via the <code class="literal">return_book_1</code> label we&#8217;ll use the <code class="literal">StubTigger</code> interface as follows</p><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre><p>Next we&#8217;ll want to listen to the output of the message sent to <code class="literal">jms:output</code></p><pre class="programlisting">Exchange receivedMessage = consumerTemplate.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>, <span class="hl-number">5000</span>)</pre><p>And the received message would pass the following assertions</p><pre class="programlisting">receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_2_output_triggered_by_input_2" href="#_scenario_2_output_triggered_by_input_2"></a>Scenario 2 (output triggered by input)</h4></div></div></div><p>Since the route is set for you it&#8217;s enough to just send a message to the <code class="literal">jms:output</code> destination.</p><pre class="programlisting">producerTemplate.sendBodyAndHeaders(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>])</pre><p>Next we&#8217;ll want to listen to the output of the message sent to <code class="literal">jms:output</code></p><pre class="programlisting">Exchange receivedMessage = consumerTemplate.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>, <span class="hl-number">5000</span>)</pre><p>And the received message would pass the following assertions</p><pre class="programlisting">receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_scenario_3_input_with_no_output" href="#_scenario_3_input_with_no_output"></a>Scenario 3 (input with no output)</h4></div></div></div><p>Since the route is set for you it&#8217;s enough to just send a message to the <code class="literal">jms:output</code> destination.</p><pre class="programlisting">producerTemplate.sendBodyAndHeaders(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:delete'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>])</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_integration" href="#_stub_runner_integration"></a>91.3&nbsp;Stub Runner Integration</h2></div></div></div><p>Spring Cloud Contract Verifier Stub Runner&#8217;s messaging module gives you an easy way to
integrate with Spring Integration. For the provided artifacts, it automatically downloads
the stubs and registers the required routes.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_the_runner_to_the_project" href="#_adding_the_runner_to_the_project"></a>91.3.1&nbsp;Adding the Runner to the Project</h3></div></div></div><p>You can have both Spring Integration and Spring Cloud Contract Stub Runner on the
classpath. Remember to annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_disabling_the_functionality_2" href="#_disabling_the_functionality_2"></a>91.3.2&nbsp;Disabling the functionality</h3></div></div></div><p>If you need to disable this functionality, set the
<code class="literal">stubrunner.integration.enabled=false</code> property.</p><p>Assume that you have the following Maven repository with deployed stubs for the
<code class="literal">integrationService</code> application:</p><pre class="programlisting">&#9492;&#9472;&#9472; .m2
&#9492;&#9472;&#9472; repository
&#9492;&#9472;&#9472; io
&#9492;&#9472;&#9472; codearte
&#9492;&#9472;&#9472; accurest
&#9492;&#9472;&#9472; stubs
&#9492;&#9472;&#9472; integrationService
&#9500;&#9472;&#9472; <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; integrationService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; integrationService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; maven-metadata-local.xml
&#9492;&#9472;&#9472; maven-metadata-local.xml</pre><p>Further assume the stubs contain the following structure:</p><pre class="programlisting">&#9500;&#9472;&#9472; META-INF
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; MANIFEST.MF
&#9492;&#9472;&#9472; repository
&#9500;&#9472;&#9472; accurest
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookDeleted.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookReturned1.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; bookReturned2.groovy
&#9492;&#9472;&#9472; mappings</pre><p>Consider the following contracts (numbered <span class="strong"><strong>1</strong></span>):</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>
input {
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>)
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'output'</span>)
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>Now consider <span class="strong"><strong>2</strong></span>:</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_2'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'input'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'output'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>and the following Spring Integration Route:</p><pre class="programlisting"><span class="hl-directive" style="color: maroon">&lt;?xml version="1.0" encoding="UTF-8"?&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;beans:beans</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.springframework.org/schema/integration"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:beans</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.springframework.org/schema/beans"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://www.springframework.org/schema/integration/spring-integration.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- REQUIRED FOR TESTING --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;bridge</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">input-channel</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"output"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">output-channel</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"outputTest"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;channel</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">id</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"outputTest"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;queue/&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/channel&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/beans:beans&gt;</span></pre><p>These examples lend themselves to three scenarios:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#integration-scenario-1" title="Scenario 1 (no input message)">the section called &#8220;Scenario 1 (no input message)&#8221;</a></li><li class="listitem"><a class="xref" href="#integration-scenario-2" title="Scenario 2 (output triggered by input)">the section called &#8220;Scenario 2 (output triggered by input)&#8221;</a></li><li class="listitem"><a class="xref" href="#integration-scenario-3" title="Scenario 3 (input with no output)">the section called &#8220;Scenario 3 (input with no output)&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="integration-scenario-1" href="#integration-scenario-1"></a>Scenario 1 (no input message)</h4></div></div></div><p>To trigger a message via the <code class="literal">return_book_1</code> label, use the <code class="literal">StubTigger</code> interface, as
follows:</p><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre><p>To listen to the output of the message sent to <code class="literal">output</code>:</p><pre class="programlisting">Message&lt;?&gt; receivedMessage = messaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'outputTest'</span>)</pre><p>The received message would pass the following assertions:</p><pre class="programlisting">receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="integration-scenario-2" href="#integration-scenario-2"></a>Scenario 2 (output triggered by input)</h4></div></div></div><p>Since the route is set for you, you can send a message to the <code class="literal">output</code>
destination:</p><pre class="programlisting">messaging.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>], <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'input'</span>)</pre><p>To listen to the output of the message sent to <code class="literal">output</code>:</p><pre class="programlisting">Message&lt;?&gt; receivedMessage = messaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'outputTest'</span>)</pre><p>The received message passes the following assertions:</p><pre class="programlisting">receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="integration-scenario-3" href="#integration-scenario-3"></a>Scenario 3 (input with no output)</h4></div></div></div><p>Since the route is set for you, you can send a message to the <code class="literal">input</code> destination:</p><pre class="programlisting">messaging.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>], <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'delete'</span>)</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_stream" href="#_stub_runner_stream"></a>91.4&nbsp;Stub Runner Stream</h2></div></div></div><p>Spring Cloud Contract Verifier Stub Runner&#8217;s messaging module gives you an easy way to
integrate with Spring Stream. For the provided artifacts, it automatically downloads the
stubs and registers the required routes.</p><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>If Stub Runner&#8217;s integration with Stream the <code class="literal">messageFrom</code> or <code class="literal">sentTo</code> Strings
are resolved first as a <code class="literal">destination</code> of a channel and no such <code class="literal">destination</code> exists, the
destination is resolved as a channel name.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you want to use Spring Cloud Stream remember, to add a dependency on
<code class="literal">org.springframework.cloud:spring-cloud-stream-test-support</code>.</p></td></tr></table></div><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-test-support<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-stream-test-support"</span></pre><p class="secondary">
</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_the_runner_to_the_project_2" href="#_adding_the_runner_to_the_project_2"></a>91.4.1&nbsp;Adding the Runner to the Project</h3></div></div></div><p>You can have both Spring Cloud Stream and Spring Cloud Contract Stub Runner on the
classpath. Remember to annotate your test class with <code class="literal">@AutoConfigureStubRunner</code>.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_disabling_the_functionality_3" href="#_disabling_the_functionality_3"></a>91.4.2&nbsp;Disabling the functionality</h3></div></div></div><p>If you need to disable this functionality, set the <code class="literal">stubrunner.stream.enabled=false</code>
property.</p><p>Assume that you have the following Maven repository with a deployed stubs for the
<code class="literal">streamService</code> application:</p><pre class="programlisting">&#9492;&#9472;&#9472; .m2
&#9492;&#9472;&#9472; repository
&#9492;&#9472;&#9472; io
&#9492;&#9472;&#9472; codearte
&#9492;&#9472;&#9472; accurest
&#9492;&#9472;&#9472; stubs
&#9492;&#9472;&#9472; streamService
&#9500;&#9472;&#9472; <span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; streamService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT.pom
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; streamService-<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT-stubs.jar
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; maven-metadata-local.xml
&#9492;&#9472;&#9472; maven-metadata-local.xml</pre><p>Further assume the stubs contain the following structure:</p><pre class="programlisting">&#9500;&#9472;&#9472; META-INF
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; MANIFEST.MF
&#9492;&#9472;&#9472; repository
&#9500;&#9472;&#9472; accurest
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookDeleted.groovy
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; bookReturned1.groovy
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; bookReturned2.groovy
&#9492;&#9472;&#9472; mappings</pre><p>Consider the following contracts (numbered <span class="strong"><strong>1</strong></span>):</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>
input { triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>) }
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'returnBook'</span>)
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
headers { header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>) }
}
}</pre><p>Now consider <span class="strong"><strong>2</strong></span>:</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_2'</span>
input {
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookStorage'</span>)
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders { header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>) }
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'returnBook'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers { header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>) }
}
}</pre><p>Now consider the following Spring configuration:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner.repositoryRoot</span>: classpath:m2repo/repository/
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner.ids</span>: org.springframework.cloud.contract.verifier.stubs:streamService:<span class="hl-number">0.0</span>.<span class="hl-number">1</span>-SNAPSHOT:stubs
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner.stubs-mode</span>: remote
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stream</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bindings</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> output</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: returnBook
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> input</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: bookStorage
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">0</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">debug</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre><p>These examples lend themselves to three scenarios:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#stream-scenario-1" title="Scenario 1 (no input message)">the section called &#8220;Scenario 1 (no input message)&#8221;</a></li><li class="listitem"><a class="xref" href="#stream-scenario-2" title="Scenario 2 (output triggered by input)">the section called &#8220;Scenario 2 (output triggered by input)&#8221;</a></li><li class="listitem"><a class="xref" href="#stream-scenario-3" title="Scenario 3 (input with no output)">the section called &#8220;Scenario 3 (input with no output)&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="stream-scenario-1" href="#stream-scenario-1"></a>Scenario 1 (no input message)</h4></div></div></div><p>To trigger a message via the <code class="literal">return_book_1</code> label, use the <code class="literal">StubTrigger</code> interface as
follows:</p><pre class="programlisting">stubFinder.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'return_book_1'</span>)</pre><p>To listen to the output of the message sent to a channel whose <code class="literal">destination</code> is
<code class="literal">returnBook</code>:</p><pre class="programlisting">Message&lt;?&gt; receivedMessage = messaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'returnBook'</span>)</pre><p>The received message passes the following assertions:</p><pre class="programlisting">receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="stream-scenario-2" href="#stream-scenario-2"></a>Scenario 2 (output triggered by input)</h4></div></div></div><p>Since the route is set for you, you can send a message to the <code class="literal">bookStorage</code>
<code class="literal">destination</code>:</p><pre class="programlisting">messaging.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>], <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookStorage'</span>)</pre><p>To listen to the output of the message sent to <code class="literal">returnBook</code>:</p><pre class="programlisting">Message&lt;?&gt; receivedMessage = messaging.receive(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'returnBook'</span>)</pre><p>The received message passes the following assertions:</p><pre class="programlisting">receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span></pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="stream-scenario-3" href="#stream-scenario-3"></a>Scenario 3 (input with no output)</h4></div></div></div><p>Since the route is set for you, you can send a message to the <code class="literal">output</code>
destination:</p><pre class="programlisting">messaging.send(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> BookReturned(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), [sample: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>], <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'delete'</span>)</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stub_runner_spring_amqp" href="#_stub_runner_spring_amqp"></a>91.5&nbsp;Stub Runner Spring AMQP</h2></div></div></div><p>Spring Cloud Contract Verifier Stub Runner&#8217;s messaging module provides an easy way to
integrate with Spring AMQP&#8217;s Rabbit Template. For the provided artifacts, it
automatically downloads the stubs and registers the required routes.</p><p>The integration tries to work standalone (that is, without interaction with a running
RabbitMQ message broker). It expects a <code class="literal">RabbitTemplate</code> on the application context and
uses it as a spring boot test named <code class="literal">@SpyBean</code>. As a result, it can use the mockito spy
functionality to verify and inspect messages sent by the application.</p><p>On the message consumer side, the stub runner considers all <code class="literal">@RabbitListener</code> annotated
endpoints and all <code class="literal">SimpleMessageListenerContainer</code> objects on the application context.</p><p>As messages are usually sent to exchanges in AMQP, the message contract contains the
exchange name as the destination. Message listeners on the other side are bound to
queues. Bindings connect an exchange to a queue. If message contracts are triggered, the
Spring AMQP stub runner integration looks for bindings on the application context that
match this exchange. Then it collects the queues from the Spring exchanges and tries to
find message listeners bound to these queues. The message is triggered for all matching
message listeners.</p><p>If you need to work with routing keys, it&#8217;s enough to pass them via the <code class="literal">amqp_receivedRoutingKey</code>
messaging header.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_the_runner_to_the_project_3" href="#_adding_the_runner_to_the_project_3"></a>91.5.1&nbsp;Adding the Runner to the Project</h3></div></div></div><p>You can have both Spring AMQP and Spring Cloud Contract Stub Runner on the classpath and
set the property <code class="literal">stubrunner.amqp.enabled=true</code>. Remember to annotate your test class
with <code class="literal">@AutoConfigureStubRunner</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you already have Stream and Integration on the classpath, you need
to disable them explicitly by setting the <code class="literal">stubrunner.stream.enabled=false</code> and
<code class="literal">stubrunner.integration.enabled=false</code> properties.</p></td></tr></table></div><p>Assume that you have the following Maven repository with a deployed stubs for the
<code class="literal">spring-cloud-contract-amqp-test</code> application.</p><pre class="programlisting">&#9492;&#9472;&#9472; .m2
&#9492;&#9472;&#9472; repository
&#9492;&#9472;&#9472; com
&#9492;&#9472;&#9472; example
&#9492;&#9472;&#9472; spring-cloud-contract-amqp-<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>
&#9500;&#9472;&#9472; <span class="hl-number">0.4</span>.<span class="hl-number">0</span>-SNAPSHOT
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; spring-cloud-contract-amqp-<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>-<span class="hl-number">0.4</span>.<span class="hl-number">0</span>-SNAPSHOT.pom
&#9474;&nbsp;&nbsp; &#9500;&#9472;&#9472; spring-cloud-contract-amqp-<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>-<span class="hl-number">0.4</span>.<span class="hl-number">0</span>-SNAPSHOT-stubs.jar
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; maven-metadata-local.xml
&#9492;&#9472;&#9472; maven-metadata-local.xml</pre><p>Further assume that the stubs contain the following structure:</p><pre class="programlisting">&#9500;&#9472;&#9472; META-INF
&#9474;&nbsp;&nbsp; &#9492;&#9472;&#9472; MANIFEST.MF
&#9492;&#9472;&#9472; contracts
&#9492;&#9472;&#9472; shouldProduceValidPersonData.groovy</pre><p>Consider the following contract:</p><pre class="programlisting">Contract.make {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Human readable description</span>
description <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Should produce valid person data'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Label by means of which the output message can be triggered</span>
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'contract-test.person.created.event'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// input to the contract</span>
input {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the contract will be triggered by a method</span>
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'createPerson()'</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// output message of the contract</span>
outputMessage {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// destination to which the output message will be sent</span>
sentTo <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'contract-test.exchange'</span>
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'contentType'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>)
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'__TypeId__'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud.contract.stubrunner.messaging.amqp.Person'</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the body of the output message</span>
body ([
id: $(consumer(<span class="hl-number">9</span>), producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]+"</span>))),
name: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"me"</span>
])
}
}</pre><p>Now consider the following Spring configuration:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> repositoryRoot</span>: classpath:m2repo/repository/
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ids</span>: org.springframework.cloud.contract.verifier.stubs.amqp:spring-cloud-contract-amqp-test:<span class="hl-number">0.4</span>.<span class="hl-number">0</span>-SNAPSHOT:stubs
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stubs-mode</span>: remote
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> amqp</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">0</span></pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_triggering_the_message" href="#_triggering_the_message"></a>Triggering the message</h4></div></div></div><p>To trigger a message using the contract above, use the <code class="literal">StubTrigger</code> interface as
follows:</p><pre class="programlisting">stubTrigger.trigger(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"contract-test.person.created.event"</span>)</pre><p>The message has a destination of <code class="literal">contract-test.exchange</code>, so the Spring AMQP stub runner
integration looks for bindings related to this exchange.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Binding binding() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> BindingBuilder.bind(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Queue(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"test.queue"</span>))
.to(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> DirectExchange(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"contract-test.exchange"</span>)).with(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"#"</span>);
}</pre><p>The binding definition binds the queue <code class="literal">test.queue</code>. As a result, the following listener
definition is matched and invoked with the contract message.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> SimpleMessageListenerContainer simpleMessageListenerContainer(
ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"test.queue"</span>);
container.setMessageListener(listenerAdapter);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> container;
}</pre><p>Also, the following annotated listener matches and is invoked:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RabbitListener(bindings = @QueueBinding(value = @Queue(value = "test.queue"), exchange = @Exchange(value = "contract-test.exchange", ignoreDeclarationExceptions = "true")))</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handlePerson(Person person) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.person = person;
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The message is directly handed over to the <code class="literal">onMessage</code> method of the
<code class="literal">MessageListener</code> associated with the matching <code class="literal">SimpleMessageListenerContainer</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_spring_amqp_test_configuration" href="#_spring_amqp_test_configuration"></a>Spring AMQP Test Configuration</h4></div></div></div><p>In order to avoid Spring AMQP trying to connect to a running broker during our tests
configure a mock <code class="literal">ConnectionFactory</code>.</p><p>To disable the mocked ConnectionFactory, set the following property:
<code class="literal">stubrunner.amqp.mockConnection=false</code></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">stubrunner</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> amqp</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> mockConnection</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">false</span></pre></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="contract-dsl" href="#contract-dsl"></a>92.&nbsp;Contract DSL</h2></div></div></div><p>Spring Cloud Contract supports out of the box 2 types of DSL. One written in
<code class="literal">Groovy</code> and one written in <code class="literal">YAML</code>.</p><p>If you decide to write the contract in Groovy, do not be alarmed if you have not used Groovy
before. Knowledge of the language is not really needed, as the Contract DSL uses only a
tiny subset of it (only literals, method calls and closures). Also, the DSL is statically
typed, to make it programmer-readable without any knowledge of the DSL itself.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Remember that, inside the Groovy contract file, you have to provide the fully
qualified name to the <code class="literal">Contract</code> class and <code class="literal">make</code> static imports, such as
<code class="literal">org.springframework.cloud.spec.Contract.make { &#8230;&#8203; }</code>. You can also provide an import to
the <code class="literal">Contract</code> class: <code class="literal">import org.springframework.cloud.spec.Contract</code> and then call
<code class="literal">Contract.make { &#8230;&#8203; }</code>.</p></td></tr></table></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Contract supports defining multiple contracts in a single file.</p></td></tr></table></div><p>The following is a complete example of a Groovy contract definition:</p><pre class="programlisting"></pre><p>The following is a complete example of a YAML contract definition:</p><pre class="programlisting">description: Some description
name: some name
priority: 8
ignored: true
request:
url: /foo
queryParameters:
a: b
b: c
method: PUT
headers:
foo: bar
fooReq: baz
body:
foo: bar
matchers:
body:
- path: $.foo
type: by_regex
value: bar
headers:
- key: foo
regex: bar
response:
status: 200
headers:
foo2: bar
foo3: foo33
fooRes: baz
body:
foo2: bar
foo3: baz
nullValue: null
matchers:
body:
- path: $.foo2
type: by_regex
value: bar
- path: $.foo3
type: by_command
value: executeMe($it)
- path: $.nullValue
type: by_null
value: null
headers:
- key: foo2
regex: bar
- key: foo3
command: andMeToo($it)</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You can compile contracts to stubs mapping using standalone maven command:
<code class="literal">mvn org.springframework.cloud:spring-cloud-contract-maven-plugin:convert</code></p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_limitations" href="#_limitations"></a>92.1&nbsp;Limitations</h2></div></div></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Contract Verifier does not properly support XML. Please use JSON or
help us implement this feature.</p></td></tr></table></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>The support for verifying the size of JSON arrays is experimental. If you want
to turn it on, please set the value of the following system property to <code class="literal">true</code>:
<code class="literal">spring.cloud.contract.verifier.assert.size</code>. By default, this feature is set to <code class="literal">false</code>.
You can also provide the <code class="literal">assertJsonSize</code> property in the plugin configuration.</p></td></tr></table></div><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Because JSON structure can have any form, it can be impossible to parse it
properly when using the Groovy DSL and the <code class="literal">value(consumer(&#8230;&#8203;), producer(&#8230;&#8203;))</code> notation in <code class="literal">GString</code>. That
is why you should use the Groovy Map notation.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_common_top_level_elements" href="#_common_top_level_elements"></a>92.2&nbsp;Common Top-Level elements</h2></div></div></div><p>The following sections describe the most common top-level elements:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#contract-dsl-description" title="92.2.1&nbsp;Description">Section&nbsp;92.2.1, &#8220;Description&#8221;</a></li><li class="listitem"><a class="xref" href="#contract-dsl-name" title="92.2.2&nbsp;Name">Section&nbsp;92.2.2, &#8220;Name&#8221;</a></li><li class="listitem"><a class="xref" href="#contract-dsl-ignoring-contracts" title="92.2.3&nbsp;Ignoring Contracts">Section&nbsp;92.2.3, &#8220;Ignoring Contracts&#8221;</a></li><li class="listitem"><a class="xref" href="#contract-dsl-passing-values-from-files" title="92.2.4&nbsp;Passing Values from Files">Section&nbsp;92.2.4, &#8220;Passing Values from Files&#8221;</a></li><li class="listitem"><a class="xref" href="#contract-dsl-http-top-level-elements" title="92.2.5&nbsp;HTTP Top-Level Elements">Section&nbsp;92.2.5, &#8220;HTTP Top-Level Elements&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-description" href="#contract-dsl-description"></a>92.2.1&nbsp;Description</h3></div></div></div><p>You can add a <code class="literal">description</code> to your contract. The description is arbitrary text. The
following code shows an example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting"> org.springframework.cloud.contract.spec.Contract.make {
description(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span>given:
An input
when:
Sth happens
then:
Output
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">')
</span> }</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">description: Some description
name: some name
priority: 8
ignored: true
request:
url: /foo
queryParameters:
a: b
b: c
method: PUT
headers:
foo: bar
fooReq: baz
body:
foo: bar
matchers:
body:
- path: $.foo
type: by_regex
value: bar
headers:
- key: foo
regex: bar
response:
status: 200
headers:
foo2: bar
foo3: foo33
fooRes: baz
body:
foo2: bar
foo3: baz
nullValue: null
matchers:
body:
- path: $.foo2
type: by_regex
value: bar
- path: $.foo3
type: by_command
value: executeMe($it)
- path: $.nullValue
type: by_null
value: null
headers:
- key: foo2
regex: bar
- key: foo3
command: andMeToo($it)</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-name" href="#contract-dsl-name"></a>92.2.2&nbsp;Name</h3></div></div></div><p>You can provide a name for your contract. Assume that you provided the following name:
<code class="literal">should register a user</code>. If you do so, the name of the autogenerated test is
<code class="literal">validate_should_register_a_user</code>. Also, the name of the stub in a WireMock stub is
<code class="literal">should_register_a_user.json</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You must ensure that the name does not contain any characters that make the
generated test not compile. Also, remember that, if you provide the same name for
multiple contracts, your autogenerated tests fail to compile and your generated stubs
override each other.</p></td></tr></table></div><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"some_special_name"</span>)
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">name: some name</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-ignoring-contracts" href="#contract-dsl-ignoring-contracts"></a>92.2.3&nbsp;Ignoring Contracts</h3></div></div></div><p>If you want to ignore a contract, you can either set a value of ignored contracts in the
plugin configuration or set the <code class="literal">ignored</code> property on the contract itself:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
ignored()
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">ignored: true</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-passing-values-from-files" href="#contract-dsl-passing-values-from-files"></a>92.2.4&nbsp;Passing Values from Files</h3></div></div></div><p>Starting with version <code class="literal">1.2.0</code>, you can pass values from files. Assume that you have the
following resources in our project.</p><pre class="programlisting">&#9492;&#9472;&#9472; src
&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; resources
&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &#9492;&#9472;&#9472; contracts
&nbsp;&nbsp;&nbsp; &#9500;&#9472;&#9472; readFromFile.groovy
&nbsp;&nbsp;&nbsp; &#9500;&#9472;&#9472; request.json
&nbsp;&nbsp;&nbsp; &#9492;&#9472;&#9472; response.json</pre><p>Further assume that your contract is as follows:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span>)
headers {
contentType(applicationJson())
}
body(file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request.json"</span>))
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/1"</span>)
}
response {
status OK()
body(file(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response.json"</span>))
headers {
contentType(textPlain())
}
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
method: GET
url: /foo
bodyFromFile: request.json
response:
status: 200
bodyFromFile: response.json</pre><p>
</p><p>Further assume that the JSON files is as follows:</p><p><span class="strong"><strong>request.json</strong></span></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"REQUEST"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p><span class="strong"><strong>response.json</strong></span></p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"RESPONSE"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>When test or stub generation takes place, the contents of the file is passed to the body
of a request or a response. The name of the file needs to be a file with location
relative to the folder in which the contract lays.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-http-top-level-elements" href="#contract-dsl-http-top-level-elements"></a>92.2.5&nbsp;HTTP Top-Level Elements</h3></div></div></div><p>The following methods can be called in the top-level closure of a contract definition.
<code class="literal">request</code> and <code class="literal">response</code> are mandatory. <code class="literal">priority</code> is optional.</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Definition of HTTP request part of the contract</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (this can be a valid request or invalid depending</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// on type of contract being specified).</span>
request {
method GET()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Definition of HTTP response part of the contract</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// (a service implementing this contract should respond</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// with following response after receiving request</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// specified in "request" part above).</span>
response {
status <span class="hl-number">200</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Contract priority, which can be used for overriding</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// contracts (1 is highest). Priority is optional.</span>
priority <span class="hl-number">1</span>
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">priority: 8
request:
...
response:
...</pre><p>
</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you want to make your contract have a <span class="strong"><strong>higher</strong></span> value of priority
you need to pass a <span class="strong"><strong>lower</strong></span> number to the <code class="literal">priority</code> tag / method. E.g. <code class="literal">priority</code> with
value <code class="literal">5</code> has <span class="strong"><strong>higher</strong></span> priority than <code class="literal">priority</code> with value <code class="literal">10</code>.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_request" href="#_request"></a>92.3&nbsp;Request</h2></div></div></div><p>The HTTP protocol requires only <span class="strong"><strong>method and url</strong></span> to be specified in a request. The
same information is mandatory in request definition of the Contract.</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// HTTP request method (GET/POST/PUT/DELETE).</span>
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Path component of request URL is specified as follows.</span>
urlPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users'</span>)
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
status <span class="hl-number">200</span>
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">method: PUT
url: /foo</pre><p>
</p><p>It is possible to specify an absolute rather than relative <code class="literal">url</code>, but using <code class="literal">urlPath</code> is
the recommended way, as doing so makes the tests <span class="strong"><strong>host-independent</strong></span>.</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Specifying `url` and `urlPath` in one contract is illegal.</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://localhost:8888/users'</span>)
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
status <span class="hl-number">200</span>
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
method: PUT
urlPath: /foo</pre><p>
</p><p><code class="literal">request</code> may contain <span class="strong"><strong>query parameters</strong></span>.</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
method GET()
urlPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users'</span>) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Each parameter is specified in form</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `'paramName' : paramValue` where parameter value</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// may be a simple literal or one of matcher functions,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// all of which are used in this example.</span>
queryParameters {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// If a simple literal is used as value</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// default matcher function is used (equalTo)</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'limit'</span>: <span class="hl-number">100</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `equalTo` function simply compares passed value</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// using identity operator (==).</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'filter'</span>: equalTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"email"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `containing` function matches strings</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// that contains passed substring.</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'gender'</span>: value(consumer(containing(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[mf]"</span>)), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'mf'</span>))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `matching` function tests parameter</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// against passed regular expression.</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'offset'</span>: value(consumer(matching(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]+"</span>)), producer(<span class="hl-number">123</span>))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// `notMatching` functions tests if parameter</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// does not match passed regular expression.</span>
parameter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'loginStartsWith'</span>: value(consumer(notMatching(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".{0,2}"</span>)), producer(<span class="hl-number">3</span>))
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
status <span class="hl-number">200</span>
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
...
queryParameters:
a: b
b: c
headers:
foo: bar
fooReq: baz
cookies:
foo: bar
fooReq: baz
body:
foo: bar
matchers:
body:
- path: $.foo
type: by_regex
value: bar
headers:
- key: foo
regex: bar
response:
status: 200
fixedDelayMilliseconds: 1000
headers:
foo2: bar
foo3: foo33
fooRes: baz
body:
foo2: bar
foo3: baz
nullValue: null
matchers:
body:
- path: $.foo2
type: by_regex
value: bar
- path: $.foo3
type: by_command
value: executeMe($it)
- path: $.nullValue
type: by_null
value: null
headers:
- key: foo2
regex: bar
- key: foo3
command: andMeToo($it)
cookies:
- key: foo2
regex: bar
- key: foo3
predefined:</pre><p>
</p><p><code class="literal">request</code> may contain additional <span class="strong"><strong>request headers</strong></span>, as shown in the following example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
method GET()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Each header is added in form `'Header-Name' : 'Header-Value'`.</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// there are also some helper methods</span>
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'key'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'value'</span>
contentType(applicationJson())
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
status <span class="hl-number">200</span>
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
...
headers:
foo: bar
fooReq: baz</pre><p>
</p><p><code class="literal">request</code> may contain additional <span class="strong"><strong>request cookies</strong></span>, as shown in the following example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
method GET()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Each Cookies is added in form `'Cookie-Key' : 'Cookie-Value'`.</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// there are also some helper methods</span>
cookies {
cookie <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'key'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'value'</span>
cookie(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'another_key'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'another_value'</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
status <span class="hl-number">200</span>
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
...
cookies:
foo: bar
fooReq: baz</pre><p>
</p><p><code class="literal">request</code> may contain a <span class="strong"><strong>request body</strong></span>:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
method GET()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Currently only JSON format of request body is supported.</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Format will be determined from a header or body's content.</span>
body <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "login" : "john", "name": "John The Contract" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
status <span class="hl-number">200</span>
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
...
body:
foo: bar</pre><p>
</p><p><code class="literal">request</code> may contain <span class="strong"><strong>multipart</strong></span> elements. To include multipart elements, use the
<code class="literal">multipart</code> method/section, as shown in the following examples</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting"></pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
method: PUT
url: /multipart
headers:
Content-Type: multipart/form-data;boundary=AaB03x
multipart:
params:
# key (parameter name), value (parameter value) pair
formParameter: '"formParameterValue"'
someBooleanParameter: true
named:
- paramName: file
fileName: filename.csv
fileContent: file content
matchers:
multipart:
params:
- key: formParameter
regex: ".+"
- key: someBooleanParameter
predefined: any_boolean
named:
- paramName: file
fileName:
predefined: non_empty
fileContent:
predefined: non_empty
response:
status: 200</pre><p>
</p><p>In the preceding example, we define parameters in either of two ways:</p><div class="itemizedlist"><p class="title"><b>Groovy DSL</b></p><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Directly, by using the map notation, where the value can be a dynamic property (such as
<code class="literal">formParameter: $(consumer(&#8230;&#8203;), producer(&#8230;&#8203;))</code>).</li><li class="listitem">By using the <code class="literal">named(&#8230;&#8203;)</code> method that lets you set a named parameter. A named parameter
can set a <code class="literal">name</code> and <code class="literal">content</code>. You can call it either via a method with two arguments,
such as <code class="literal">named("fileName", "fileContent")</code>, or via a map notation, such as
<code class="literal">named(name: "fileName", content: "fileContent")</code>.</li></ul></div><div class="itemizedlist"><p class="title"><b>YAML</b></p><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The multipart parameters are set via <code class="literal">multipart.params</code> section</li><li class="listitem">The named parameters (the <code class="literal">fileName</code> and <code class="literal">fileContent</code> for a given parameter name)
can be set via the <code class="literal">multipart.named</code> section. That section contains
the <code class="literal">paramName</code> (name of the parameter), <code class="literal">fileName</code> (name of the file),
<code class="literal">fileContent</code> (content of the file) fields</li><li class="listitem"><p class="simpara">The dynamic bits can be set via the <code class="literal">matchers.multipart</code> section</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">for parameters use the <code class="literal">params</code> section that can accept
<code class="literal">regex</code> or a <code class="literal">predefined</code> regular expression</li><li class="listitem">for named params use the <code class="literal">named</code> section where first you
define the parameter name via <code class="literal">paramName</code> and then you can pass the
parametrization of either <code class="literal">fileName</code> or <code class="literal">fileContent</code> via
<code class="literal">regex</code> or a <code class="literal">predefined</code> regular expression</li></ul></div></li></ul></div><p>From this contract, the generated test is as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"multipart/form-data;boundary=AaB03x"</span>)
.param(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"formParameter"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\"formParameterValue\""</span>)
.param(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"someBooleanParameter"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"true"</span>)
.multiPart(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"file"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"filename.csv"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"file content"</span>.getBytes());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/multipart"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);</pre><p>The WireMock stub is as follows:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/multipart"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PUT"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"multipart/form-data;boundary=AaB03x.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"</span>formParameter\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n\\"</span>.+\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\\r\\n--\\\\1.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"</span>someBooleanParameter\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n(true|false)\\r\\n--\\\\1.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".*--(.*)\\r\\nContent-Disposition: form-data; name=\\"</span>file\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"; filename=\\"</span>[\\\\S\\\\s]+\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\\r\\n(Content-Type: .*\\r\\n)?(Content-Transfer-Encoding: .*\\r\\n)?(Content-Length: \\\\d+\\r\\n)?\\r\\n[\\\\S\\\\s]+\\r\\n--\\\\1.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"transformers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response-template"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo-transformer"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_response" href="#_response"></a>92.4&nbsp;Response</h2></div></div></div><p>The response must contain an <span class="strong"><strong>HTTP status code</strong></span> and may contain other information. The
following code shows an example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//...</span>
method GET()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>
}
response {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Status code sent by the server</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// in response to request specified above.</span>
status OK()
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">response:
...
status: 200</pre><p>
</p><p>Besides status, the response may contain <span class="strong"><strong>headers</strong></span>, <span class="strong"><strong>cookies</strong></span> and a <span class="strong"><strong>body</strong></span>, both of which are
specified the same way as in the request (see the previous paragraph).</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Via the Groovy DSL you can reference the <code class="literal">org.springframework.cloud.contract.spec.internal.HttpStatus</code>
methods to provide a meaningful status instead of a digit. E.g. you can call
<code class="literal">OK()</code> for a status <code class="literal">200</code> or <code class="literal">BAD_REQUEST()</code> for <code class="literal">400</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_dynamic_properties" href="#_dynamic_properties"></a>92.5&nbsp;Dynamic properties</h2></div></div></div><p>The contract can contain some dynamic properties: timestamps, IDs, and so on. You do not
want to force the consumers to stub their clocks to always return the same value of time
so that it gets matched by the stub.</p><p>For Groovy DSL you can provide the dynamic parts in your contracts
in two ways: pass them directly in the body or set them in a separate section called
<code class="literal">bodyMatchers</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Before 2.0.0 these were set using <code class="literal">testMatchers</code> and <code class="literal">stubMatchers</code>,
check out the <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/wiki/Spring-Cloud-Contract-2.0-Migration-Guide" target="_top">migration guide</a> for more information.</p></td></tr></table></div><p>For YAML you can only use the <code class="literal">matchers</code> section.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_dynamic_properties_inside_the_body" href="#_dynamic_properties_inside_the_body"></a>92.5.1&nbsp;Dynamic properties inside the body</h3></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is valid only for Groovy DSL. Check out the
<a class="xref" href="#contract-matchers" title="92.5.7&nbsp;Dynamic Properties in the Matchers Sections">Section&nbsp;92.5.7, &#8220;Dynamic Properties in the Matchers Sections&#8221;</a> section for YAML examples of a similar feature.</p></td></tr></table></div><p>You can set the properties inside the body either with the <code class="literal">value</code> method or, if you use
the Groovy map notation, with <code class="literal">$()</code>. The following example shows how to set dynamic
properties with the value method:</p><pre class="programlisting">value(consumer(...), producer(...))
value(c(...), p(...))
value(stub(...), test(...))
value(client(...), server(...))</pre><p>The following example shows how to set dynamic properties with <code class="literal">$()</code>:</p><pre class="programlisting">$(consumer(...), producer(...))
$(c(...), p(...))
$(stub(...), test(...))
$(client(...), server(...))</pre><p>Both approaches work equally well. <code class="literal">stub</code> and <code class="literal">client</code> methods are aliases over the <code class="literal">consumer</code>
method. Subsequent sections take a closer look at what you can do with those values.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_regular_expressions" href="#_regular_expressions"></a>92.5.2&nbsp;Regular expressions</h3></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is valid only for Groovy DSL. Check out the
<a class="xref" href="#contract-matchers" title="92.5.7&nbsp;Dynamic Properties in the Matchers Sections">Section&nbsp;92.5.7, &#8220;Dynamic Properties in the Matchers Sections&#8221;</a> section for YAML examples of a similar feature.</p></td></tr></table></div><p>You can use regular expressions to write your requests in Contract DSL. Doing so is
particularly useful when you want to indicate that a given response should be provided
for requests that follow a given pattern. Also, you can use regular expressions when you
need to use patterns and not exact values both for your test and your server side tests.</p><p>The following example shows how to use regular expressions to write a request:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>)
url $(consumer(~/\/[<span class="hl-number">0</span>-<span class="hl-number">9</span>]{<span class="hl-number">2</span>}/), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/12'</span>))
}
response {
status OK()
body(
id: $(anyNumber()),
surname: $(
consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Kowalsky'</span>),
producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-zA-Z]+'</span>))
),
name: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Jan'</span>,
created: $(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'2014-02-02 12:23:43'</span>), producer(execute(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'currentDate(it)'</span>))),
correlationId: value(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'5d1f9fef-e0dc-4f3d-a7e4-72d2220dd827'</span>),
producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}'</span>))
)
)
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'text/plain'</span>
}
}
}</pre><p>You can also provide only one side of the communication with a regular expression. If you
do so, then the contract engine automatically provides the generated string that matches
the provided regular expression. The following code shows an example:</p><pre class="programlisting"></pre><p>In the preceding example, the opposite side of the communication has the respective data
generated for request and response.</p><p>Spring Cloud Contract comes with a series of predefined regular expressions that you can
use in your contracts, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern TRUE_OR_FALSE = Pattern.compile(/(true|false)/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ALPHA_NUMERIC = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-zA-Z0-9]+'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern NUMBER = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'-?(\\d*\\.\\d+|\\d+)'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern INTEGER = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'-?(\\d+)'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern POSITIVE_INT = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'([1-9]\\d*)'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern DOUBLE = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'-?(\\d*\\.\\d+)'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern HEX = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-fA-F0-9]+'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern IP_ADDRESS = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern HOSTNAME_PATTERN = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'((http[s]?|ftp):/)/?([^:/\\s]+)(:[0-9]{1,5})?'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern EMAIL = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern URL = UrlHelper.URL
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern HTTPS_URL = UrlHelper.HTTPS_URL
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern UUID = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ANY_DATE = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ANY_DATE_TIME = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ANY_TIME = Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern NON_EMPTY = Pattern.compile(/[\S\s]+/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern NON_BLANK = Pattern.compile(/^\s*\S[\S\s]*/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Pattern ISO8601_WITH_OFFSET = Pattern.compile(/([<span class="hl-number">0</span>-<span class="hl-number">9</span>]{<span class="hl-number">4</span>})-(<span class="hl-number">1</span>[<span class="hl-number">0</span>-<span class="hl-number">2</span>]|<span class="hl-number">0</span>[<span class="hl-number">1</span>-<span class="hl-number">9</span>])-(<span class="hl-number">3</span>[<span class="hl-number">01</span>]|<span class="hl-number">0</span>[<span class="hl-number">1</span>-<span class="hl-number">9</span>]|[<span class="hl-number">12</span>][<span class="hl-number">0</span>-<span class="hl-number">9</span>])T(<span class="hl-number">2</span>[<span class="hl-number">0</span>-<span class="hl-number">3</span>]|[<span class="hl-number">01</span>][<span class="hl-number">0</span>-<span class="hl-number">9</span>]):([<span class="hl-number">0</span>-<span class="hl-number">5</span>][<span class="hl-number">0</span>-<span class="hl-number">9</span>]):([<span class="hl-number">0</span>-<span class="hl-number">5</span>][<span class="hl-number">0</span>-<span class="hl-number">9</span>])(\.\d{<span class="hl-number">3</span>})?(Z|[+-][<span class="hl-number">01</span>]\d:[<span class="hl-number">0</span>-<span class="hl-number">5</span>]\d)/)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> Pattern anyOf(String... values){
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Pattern.compile(values.collect({<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"^$it\$"</span>}).join(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"|"</span>))
}
Pattern onlyAlphaUnicode() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ONLY_ALPHA_UNICODE
}
Pattern alphaNumeric() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ALPHA_NUMERIC
}
Pattern number() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> NUMBER
}
Pattern positiveInt() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> POSITIVE_INT
}
Pattern anyBoolean() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> TRUE_OR_FALSE
}
Pattern anInteger() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> INTEGER
}
Pattern aDouble() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> DOUBLE
}
Pattern ipAddress() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> IP_ADDRESS
}
Pattern hostname() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> HOSTNAME_PATTERN
}
Pattern email() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> EMAIL
}
Pattern url() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> URL
}
Pattern httpsUrl() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> HTTPS_URL
}
Pattern uuid(){
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> UUID
}
Pattern isoDate() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ANY_DATE
}
Pattern isoDateTime() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ANY_DATE_TIME
}
Pattern isoTime() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ANY_TIME
}
Pattern iso8601WithOffset() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> ISO8601_WITH_OFFSET
}
Pattern nonEmpty() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> NON_EMPTY
}
Pattern nonBlank() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> NON_BLANK
}</pre><p>In your contract, you can use it as shown in the following example:</p><pre class="programlisting"></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_passing_optional_parameters" href="#_passing_optional_parameters"></a>92.5.3&nbsp;Passing Optional Parameters</h3></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is valid only for Groovy DSL. Check out the
<a class="xref" href="#contract-matchers" title="92.5.7&nbsp;Dynamic Properties in the Matchers Sections">Section&nbsp;92.5.7, &#8220;Dynamic Properties in the Matchers Sections&#8221;</a> section for YAML examples of a similar feature.</p></td></tr></table></div><p>It is possible to provide optional parameters in your contract. However, you can provide
optional parameters only for the following:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="emphasis"><em>STUB</em></span> side of the Request</li><li class="listitem"><span class="emphasis"><em>TEST</em></span> side of the Response</li></ul></div><p>The following example shows how to provide optional parameters:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
priority <span class="hl-number">1</span>
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users/password'</span>
headers {
contentType(applicationJson())
}
body(
email: $(consumer(optional(regex(email()))), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'abc@abc.com'</span>)),
callback_url: $(consumer(regex(hostname())), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'http://partners.com'</span>))
)
}
response {
status <span class="hl-number">404</span>
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>
}
body(
code: value(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"123123"</span>), producer(optional(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"123123"</span>)))
)
}
}</pre><p>By wrapping a part of the body with the <code class="literal">optional()</code> method, you create a regular
expression that must be present 0 or more times.</p><p>If you use Spock for, the following test would be generated from the previous example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"
</span> given:
def request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{"email":"abc@abc.com","callback_url":"http://partners.com"}'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
when:
def response = given().spec(request)
.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users/password"</span>)
then:
response.statusCode == <span class="hl-number">404</span>
response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>) == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>
and:
DocumentContext parsedJson = JsonPath.parse(response.body.asString())
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['code']"</span>).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(123123)?"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"</span></pre><p>The following stub would also be generated:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span>{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users/password"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : [ {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['email'] =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,6})?/)]"</span>
}, {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['callback_url'] =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"</span>
} ],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"equalTo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>
}
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">404</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\\"</span>code\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span><span class="hl-number">123123</span>\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>message\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>User not found by email == [not.existing<em><span class="hl-annotation" style="color: gray">@user.com]\\"}",</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"priority"</span> : <span class="hl-number">1</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_executing_custom_methods_on_the_server_side" href="#_executing_custom_methods_on_the_server_side"></a>92.5.4&nbsp;Executing Custom Methods on the Server Side</h3></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is valid only for Groovy DSL. Check out the
<a class="xref" href="#contract-matchers" title="92.5.7&nbsp;Dynamic Properties in the Matchers Sections">Section&nbsp;92.5.7, &#8220;Dynamic Properties in the Matchers Sections&#8221;</a> section for YAML examples of a similar feature.</p></td></tr></table></div><p>You can define a method call that executes on the server side during the test. Such a
method can be added to the class defined as "baseClassForTests" in the configuration. The
following code shows an example of the contract portion of the test case:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'PUT'</span>
url $(consumer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'^/api/[0-9]{2}$'</span>)), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/api/12'</span>))
headers {
header <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span>
}
body <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'\
</span> [{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Gonna see you at Warsaw"</span>
}]
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> }
response {
body (
path: $(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/api/12'</span>), producer(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'^/api/[0-9]{2}$'</span>))),
correlationId: $(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'1223456'</span>), producer(execute(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'isProperCorrelationId($it)'</span>)))
)
status OK()
}
}</pre><p>The following code shows the base class portion of the test case:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BaseMockMvcSpec <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Specification {
def setup() {
RestAssuredMockMvc.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> PairIdController())
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> isProperCorrelationId(Integer correlationId) {
assert correlationId == <span class="hl-number">123456</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> isEmpty(String value) {
assert value == null
}
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You cannot use both a String and <code class="literal">execute</code> to perform concatenation. For
example, calling <code class="literal">header('Authorization', 'Bearer ' + execute('authToken()'))</code> leads to
improper results. Instead, call <code class="literal">header('Authorization', execute('authToken()'))</code> and
ensure that the <code class="literal">authToken()</code> method returns everything you need.</p></td></tr></table></div><p>The type of the object read from the JSON can be one of the following, depending on the
JSON path:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">String</code>: If you point to a <code class="literal">String</code> value in the JSON.</li><li class="listitem"><code class="literal">JSONArray</code>: If you point to a <code class="literal">List</code> in the JSON.</li><li class="listitem"><code class="literal">Map</code>: If you point to a <code class="literal">Map</code> in the JSON.</li><li class="listitem"><code class="literal">Number</code>: If you point to <code class="literal">Integer</code>, <code class="literal">Double</code> etc. in the JSON.</li><li class="listitem"><code class="literal">Boolean</code>: If you point to a <code class="literal">Boolean</code> in the JSON.</li></ul></div><p>In the request part of the contract, you can specify that the <code class="literal">body</code> should be taken from
a method.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>You must provide both the consumer and the producer side. The <code class="literal">execute</code> part
is applied for the whole body - not for parts of it.</p></td></tr></table></div><p>The following example shows how to read an object from JSON:</p><pre class="programlisting">Contract contractDsl = Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/something'</span>
body(
$(c(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>), p(execute(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'hashCode()'</span>)))
)
}
response {
status OK()
}
}</pre><p>The preceding example results in calling the <code class="literal">hashCode()</code> method in the request body.
It should resemble the following code:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.body(hashCode());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/something"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_referencing_the_request_from_the_response" href="#_referencing_the_request_from_the_response"></a>92.5.5&nbsp;Referencing the Request from the Response</h3></div></div></div><p>The best situation is to provide fixed values, but sometimes you need to reference a
request in your response.</p><p>If you&#8217;re writing contracts using Groovy DSL, you can use the <code class="literal">fromRequest()</code> method, which lets
you reference a bunch of elements from the HTTP request. You can use the following
options:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">fromRequest().url()</code>: Returns the request URL and query parameters.</li><li class="listitem"><code class="literal">fromRequest().query(String key)</code>: Returns the first query parameter with a given name.</li><li class="listitem"><code class="literal">fromRequest().query(String key, int index)</code>: Returns the nth query parameter with a
given name.</li><li class="listitem"><code class="literal">fromRequest().path()</code>: Returns the full path.</li><li class="listitem"><code class="literal">fromRequest().path(int index)</code>: Returns the nth path element.</li><li class="listitem"><code class="literal">fromRequest().header(String key)</code>: Returns the first header with a given name.</li><li class="listitem"><code class="literal">fromRequest().header(String key, int index)</code>: Returns the nth header with a given name.</li><li class="listitem"><code class="literal">fromRequest().body()</code>: Returns the full request body.</li><li class="listitem"><code class="literal">fromRequest().body(String jsonPath)</code>: Returns the element from the request that
matches the JSON Path.</li></ul></div><p>If you&#8217;re using the YAML contract definition you have to use the
<a class="link" href="http://handlebarsjs.com/" target="_top">Handlebars</a> <code class="literal">{{{ }}}</code> notation with custom, Spring Cloud Contract
functions to achieve this.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">{{{ request.url }}}</code>: Returns the request URL and query parameters.</li><li class="listitem"><code class="literal">{{{ request.query.key.[index] }}}</code>: Returns the nth query parameter with a given name.
E.g. for key <code class="literal">foo</code>, first entry <code class="literal">{{{ request.query.foo.[0] }}}</code></li><li class="listitem"><code class="literal">{{{ request.path }}}</code>: Returns the full path.</li><li class="listitem"><code class="literal">{{{ request.path.[index] }}}</code>: Returns the nth path element. E.g.
for first entry <code class="literal">`</code>{{{ request.path.[0] }}}</li><li class="listitem"><code class="literal">{{{ request.headers.key }}}</code>: Returns the first header with a given name.</li><li class="listitem"><code class="literal">{{{ request.headers.key.[index] }}}</code>: Returns the nth header with a given name.</li><li class="listitem"><code class="literal">{{{ request.body }}}</code>: Returns the full request body.</li><li class="listitem"><code class="literal">{{{ jsonpath this 'your.json.path' }}}</code>: Returns the element from the request that
matches the JSON Path. E.g. for json path <code class="literal">$.foo</code> - <code class="literal">{{{ jsonpath this '$.foo' }}}</code></li></ul></div><p>Consider the following contract:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting"></pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
method: GET
url: /api/v1/xxxx
queryParameters:
foo:
- bar
- bar2
headers:
Authorization:
- secret
- secret2
body:
foo: bar
baz: 5
response:
status: 200
headers:
Authorization: "foo {{{ request.headers.Authorization.0 }}} bar"
body:
url: "{{{ request.url }}}"
path: "{{{ request.path }}}"
pathIndex: "{{{ request.path.1 }}}"
param: "{{{ request.query.foo }}}"
paramIndex: "{{{ request.query.foo.1 }}}"
authorization: "{{{ request.headers.Authorization.0 }}}"
authorization2: "{{{ request.headers.Authorization.1 }}"
fullBody: "{{{ request.body }}}"
responseFoo: "{{{ jsonpath this '$.foo' }}}"
responseBaz: "{{{ jsonpath this '$.baz' }}}"
responseBaz2: "Bla bla {{{ jsonpath this '$.foo' }}} bla bla"</pre><p>
</p><p>Running a JUnit test generation leads to a test that resembles the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret"</span>)
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"foo\":\"bar\",\"baz\":5}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>,<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>,<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>)
.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo secret bar"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['fullBody']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"foo\":\"bar\",\"baz\":5}"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['authorization']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['authorization2']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['path']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['param']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['paramIndex']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['pathIndex']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"v1"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['responseBaz']"</span>).isEqualTo(<span class="hl-number">5</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['responseFoo']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['url']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx?foo=bar&amp;foo=bar2"</span>);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['responseBaz2']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Bla bla bar bla bla"</span>);</pre><p>As you can see, elements from the request have been properly referenced in the response.</p><p>The generated WireMock stub should resemble the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"urlPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"equalTo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"queryParameters"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"equalTo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['baz'] == 5)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['foo'] == 'bar')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"authorization\":\"{{{request.headers.Authorization.[0]}}}\",\"path\":\"{{{request.path}}}\",\"responseBaz\":{{{jsonpath this '$.baz'}}} ,\"param\":\"{{{request.query.foo.[0]}}}\",\"pathIndex\":\"{{{request.path.[1]}}}\",\"responseBaz2\":\"Bla bla {{{jsonpath this '$.foo'}}} bla bla\",\"responseFoo\":\"{{{jsonpath this '$.foo'}}}\",\"authorization2\":\"{{{request.headers.Authorization.[1]}}}\",\"fullBody\":\"{{{escapejsonbody}}}\",\"url\":\"{{{request.url}}}\",\"paramIndex\":\"{{{request.query.foo.[1]}}}\"}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Authorization"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{{{request.headers.Authorization.[0]}}};foo"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"transformers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response-template"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>Sending a request such as the one presented in the <code class="literal">request</code> part of the contract results
in sending the following response body:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx?foo=bar&amp;foo=bar2"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"path"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/api/v1/xxxx"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"pathIndex"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"v1"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"param"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"paramIndex"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"authorization"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"authorization2"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret2"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fullBody"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"foo\":\"bar\",\"baz\":5}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseFoo"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseBaz"</span> : <span class="hl-number">5</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"responseBaz2"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Bla bla bar bla bla"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This feature works only with WireMock having a version greater than or equal
to 2.5.1. The Spring Cloud Contract Verifier uses WireMock&#8217;s
<code class="literal">response-template</code> response transformer. It uses Handlebars to convert the Mustache <code class="literal">{{{ }}}</code> templates into
proper values. Additionally, it registers two helper functions:</p></td></tr></table></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">escapejsonbody</code>: Escapes the request body in a format that can be embedded in a JSON.</li><li class="listitem"><code class="literal">jsonpath</code>: For a given parameter, find an object in the request body.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_registering_your_own_wiremock_extension" href="#_registering_your_own_wiremock_extension"></a>92.5.6&nbsp;Registering Your Own WireMock Extension</h3></div></div></div><p>WireMock lets you register custom extensions. By default, Spring Cloud Contract registers
the transformer, which lets you reference a request from a response. If you want to
provide your own extensions, you can register an implementation of the
<code class="literal">org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions</code> interface.
Since we use the spring.factories extension approach, you can create an entry in
<code class="literal">META-INF/spring.factories</code> file similar to the following:</p><pre class="programlisting">org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockExtensions=\
org.springframework.cloud.contract.stubrunner.provider.wiremock.TestWireMockExtensions
org.springframework.cloud.contract.spec.ContractConverter=\
org.springframework.cloud.contract.stubrunner.TestCustomYamlContractConverter</pre><p>The following is an example of a custom extension:</p><p><b>TestWireMockExtensions.groovy.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.verifier.dsl.wiremock
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.github.tomakehurst.wiremock.extension.Extension
<strong class="hl-tag" style="color: blue">/**
* Extension that registers the default transformer and the custom one
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> TestWireMockExtensions <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> WireMockExtensions {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
List&lt;Extension&gt; extensions() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> [
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> DefaultResponseTransformer(),
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> CustomExtension()
]
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomExtension <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> Extension {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
String getName() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo-transformer"</span>
}
}</pre><p>
</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Remember to override the <code class="literal">applyGlobally()</code> method and set it to <code class="literal">false</code> if you
want the transformation to be applied only for a mapping that explicitly requires it.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-matchers" href="#contract-matchers"></a>92.5.7&nbsp;Dynamic Properties in the Matchers Sections</h3></div></div></div><p>If you work with <a class="link" href="https://docs.pact.io/" target="_top">Pact</a>, the following discussion may seem familiar.
Quite a few users are used to having a separation between the body and setting the
dynamic parts of a contract.</p><p>You can use the <code class="literal">bodyMatchers</code> section for two reasons:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Define the dynamic values that should end up in a stub.
You can set it in the <code class="literal">request</code> or <code class="literal">inputMessage</code> part of your contract.</li><li class="listitem">Verify the result of your test.
This section is present in the <code class="literal">response</code> or <code class="literal">outputMessage</code> side of the
contract.</li></ul></div><p>Currently, Spring Cloud Contract Verifier supports only JSON Path-based matchers with the
following matching possibilities:</p><div class="itemizedlist"><p class="title"><b>Groovy DSL</b></p><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">For the stubs(in tests on the Consumer&#8217;s side):</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">byEquality()</code>: The value taken from the consumer&#8217;s request via the provided JSON Path must be
equal to the value provided in the contract.</li><li class="listitem"><code class="literal">byRegex(&#8230;&#8203;)</code>: The value taken from the consumer&#8217;s request via the provided JSON Path must
match the regex.</li><li class="listitem"><code class="literal">byDate()</code>: The value taken from the consumer&#8217;s request via the provided JSON Path must
match the regex for an ISO Date value.</li><li class="listitem"><code class="literal">byTimestamp()</code>: The value taken from the consumer&#8217;s request via the provided JSON Path must
match the regex for an ISO DateTime value.</li><li class="listitem"><code class="literal">byTime()</code>: The value taken from the consumer&#8217;s request via the provided JSON Path must
match the regex for an ISO Time value.</li></ul></div></li><li class="listitem"><p class="simpara">For the verification(in generated tests on the Producer&#8217;s side):</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">byEquality()</code>: The value taken from the producer&#8217;s response via the provided JSON Path must be
equal to the provided value in the contract.</li><li class="listitem"><code class="literal">byRegex(&#8230;&#8203;)</code>: The value taken from the producer&#8217;s response via the provided JSON Path must
match the regex.</li><li class="listitem"><code class="literal">byDate()</code>: The value taken from the producer&#8217;s response via the provided JSON Path must match
the regex for an ISO Date value.</li><li class="listitem"><code class="literal">byTimestamp()</code>: The value taken from the producer&#8217;s response via the provided JSON Path must
match the regex for an ISO DateTime value.</li><li class="listitem"><code class="literal">byTime()</code>: The value taken from the producer&#8217;s response via the provided JSON Path must match
the regex for an ISO Time value.</li><li class="listitem"><code class="literal">byType()</code>: The value taken from the producer&#8217;s response via the provided JSON Path needs to be
of the same type as the type defined in the body of the response in the contract.
<code class="literal">byType</code> can take a closure, in which you can set <code class="literal">minOccurrence</code> and <code class="literal">maxOccurrence</code>.
That way, you can assert the size of the flattened collection. To check the size of an
unflattened collection, use a custom method with the <code class="literal">byCommand(&#8230;&#8203;)</code> testMatcher.</li><li class="listitem"><p class="simpara"><code class="literal">byCommand(&#8230;&#8203;)</code>: The value taken from the producer&#8217;s response via the provided JSON Path is
passed as an input to the custom method that you provide. For example,
<code class="literal">byCommand('foo($it)')</code> results in calling a <code class="literal">foo</code> method to which the value matching the
JSON Path gets passed. The type of the object read from the JSON can be one of the
following, depending on the JSON path:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: square; "><li class="listitem"><code class="literal">String</code>: If you point to a <code class="literal">String</code> value.</li><li class="listitem"><code class="literal">JSONArray</code>: If you point to a <code class="literal">List</code>.</li><li class="listitem"><code class="literal">Map</code>: If you point to a <code class="literal">Map</code>.</li><li class="listitem"><code class="literal">Number</code>: If you point to <code class="literal">Integer</code>, <code class="literal">Double</code>, or other kind of number.</li><li class="listitem"><code class="literal">Boolean</code>: If you point to a <code class="literal">Boolean</code>.</li></ul></div></li><li class="listitem"><code class="literal">byNull()</code>: The value taken from the response via the provided JSON Path must be null</li></ul></div></li></ul></div><p><b>YAML.&nbsp;</b><span class="emphasis"><em>Please read the Groovy section for detailed explanation of
what the types mean</em></span></p><p>For YAML the structure of a matcher looks like this</p><pre class="programlisting">- path: $.foo
type: by_regex
value: bar</pre><p>Or if you want to use one of the predefined regular expressions
<code class="literal">[only_alpha_unicode, number, any_boolean, ip_address, hostname,
email, url, uuid, iso_date, iso_date_time, iso_time, iso_8601_with_offset, non_empty, non_blank]</code>:</p><pre class="programlisting">- path: $.foo
type: by_regex
predefined: only_alpha_unicode</pre><p>Below you can find the allowed list of `type`s.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">For <code class="literal">stubMatchers</code>:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">by_equality</code></li><li class="listitem"><code class="literal">by_regex</code></li><li class="listitem"><code class="literal">by_date</code></li><li class="listitem"><code class="literal">by_timestamp</code></li><li class="listitem"><code class="literal">by_time</code></li></ul></div></li><li class="listitem"><p class="simpara">For <code class="literal">testMatchers</code>:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><code class="literal">by_equality</code></li><li class="listitem"><code class="literal">by_regex</code></li><li class="listitem"><code class="literal">by_date</code></li><li class="listitem"><code class="literal">by_timestamp</code></li><li class="listitem"><code class="literal">by_time</code></li><li class="listitem"><p class="simpara"><code class="literal">by_type</code></p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: square; "><li class="listitem">there are 2 additional fields accepted: <code class="literal">minOccurrence</code> and <code class="literal">maxOccurrence</code>.</li></ul></div></li><li class="listitem"><code class="literal">by_command</code></li><li class="listitem"><code class="literal">by_null</code></li></ul></div></li></ul></div><p>Consider the following example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">Contract contractDsl = Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
urlPath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/get'</span>
body([
duck : <span class="hl-number">123</span>,
alpha : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'abc'</span>,
number : <span class="hl-number">123</span>,
aBoolean : true,
date : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'2017-01-01'</span>,
dateTime : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'2017-01-01T01:23:45'</span>,
time : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'01:02:34'</span>,
valueWithoutAMatcher: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>,
valueWithTypeMatch : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'string'</span>,
key : [
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'complex.key'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
]
])
bodyMatchers {
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{3}"</span>))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byEquality())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.alpha'</span>, byRegex(onlyAlphaUnicode()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.alpha'</span>, byEquality())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.number'</span>, byRegex(number()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.aBoolean'</span>, byRegex(anyBoolean()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.date'</span>, byDate())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.dateTime'</span>, byTimestamp())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.time'</span>, byTime())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.['key'].['complex.key']"</span>, byEquality())
}
headers {
contentType(applicationJson())
}
}
response {
status OK()
body([
duck : <span class="hl-number">123</span>,
alpha : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'abc'</span>,
number : <span class="hl-number">123</span>,
positiveInteger : <span class="hl-number">1234567890</span>,
negativeInteger : -<span class="hl-number">1234567890</span>,
positiveDecimalNumber: <span class="hl-number">123.4567890</span>,
negativeDecimalNumber: -<span class="hl-number">123.4567890</span>,
aBoolean : true,
date : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'2017-01-01'</span>,
dateTime : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'2017-01-01T01:23:45'</span>,
time : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"01:02:34"</span>,
valueWithoutAMatcher : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>,
valueWithTypeMatch : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'string'</span>,
valueWithMin : [
<span class="hl-number">1</span>, <span class="hl-number">2</span>, <span class="hl-number">3</span>
],
valueWithMax : [
<span class="hl-number">1</span>, <span class="hl-number">2</span>, <span class="hl-number">3</span>
],
valueWithMinMax : [
<span class="hl-number">1</span>, <span class="hl-number">2</span>, <span class="hl-number">3</span>
],
valueWithMinEmpty : [],
valueWithMaxEmpty : [],
key : [
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'complex.key'</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
],
nullValue : null
])
bodyMatchers {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts the jsonpath value against manual regex</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{3}"</span>))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts the jsonpath value against the provided value</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byEquality())
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts the jsonpath value against some default regex</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.alpha'</span>, byRegex(onlyAlphaUnicode()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.alpha'</span>, byEquality())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.number'</span>, byRegex(number()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.positiveInteger'</span>, byRegex(anInteger()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.negativeInteger'</span>, byRegex(anInteger()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.positiveDecimalNumber'</span>, byRegex(aDouble()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.negativeDecimalNumber'</span>, byRegex(aDouble()))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.aBoolean'</span>, byRegex(anyBoolean()))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts vs inbuilt time related regex</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.date'</span>, byDate())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.dateTime'</span>, byTimestamp())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.time'</span>, byTime())
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// asserts that the resulting type is the same as in response body</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithTypeMatch'</span>, byType())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMin'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (min 1)</span>
minOccurrence(<span class="hl-number">1</span>)
})
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMax'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (max 3)</span>
maxOccurrence(<span class="hl-number">3</span>)
})
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMinMax'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (min 1 &amp; max 3)</span>
minOccurrence(<span class="hl-number">1</span>)
maxOccurrence(<span class="hl-number">3</span>)
})
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMinEmpty'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (min 0)</span>
minOccurrence(<span class="hl-number">0</span>)
})
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.valueWithMaxEmpty'</span>, byType {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// results in verification of size of array (max 0)</span>
maxOccurrence(<span class="hl-number">0</span>)
})
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// will execute a method `assertThatValueIsANumber`</span>
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.duck'</span>, byCommand(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'assertThatValueIsANumber($it)'</span>))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.['key'].['complex.key']"</span>, byEquality())
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.nullValue'</span>, byNull())
}
headers {
contentType(applicationJson())
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Some-Header'</span>, $(c(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'someValue'</span>), p(regex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'[a-zA-Z]{9}'</span>))))
}
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">request:
method: GET
urlPath: /get/1
headers:
Content-Type: application/json
cookies:
foo: 2
bar: 3
queryParameters:
limit: 10
offset: 20
filter: 'email'
sort: name
search: 55
age: 99
name: John.Doe
email: 'bob@email.com'
body:
duck: 123
alpha: "abc"
number: 123
aBoolean: true
date: "2017-01-01"
dateTime: "2017-01-01T01:23:45"
time: "01:02:34"
valueWithoutAMatcher: "foo"
valueWithTypeMatch: "string"
key:
"complex.key": 'foo'
nullValue: null
matchers:
url:
regex: /get/[0-9]
# predefined:
# execute a method
#command: 'equals($it)'
queryParameters:
- key: limit
type: equal_to
value: 20
- key: offset
type: containing
value: 20
- key: sort
type: equal_to
value: name
- key: search
type: not_matching
value: '^[0-9]{2}$'
- key: age
type: not_matching
value: '^\\w*$'
- key: name
type: matching
value: 'John.*'
- key: hello
type: absent
cookies:
- key: foo
regex: '[0-9]'
- key: bar
command: 'equals($it)'
headers:
- key: Content-Type
regex: "application/json.*"
body:
- path: $.duck
type: by_regex
value: "[0-9]{3}"
- path: $.duck
type: by_equality
- path: $.alpha
type: by_regex
predefined: only_alpha_unicode
- path: $.alpha
type: by_equality
- path: $.number
type: by_regex
predefined: number
- path: $.aBoolean
type: by_regex
predefined: any_boolean
- path: $.date
type: by_date
- path: $.dateTime
type: by_timestamp
- path: $.time
type: by_time
- path: "$.['key'].['complex.key']"
type: by_equality
- path: $.nullvalue
type: by_null
response:
status: 200
cookies:
foo: 1
bar: 2
body:
duck: 123
alpha: "abc"
number: 123
aBoolean: true
date: "2017-01-01"
dateTime: "2017-01-01T01:23:45"
time: "01:02:34"
valueWithoutAMatcher: "foo"
valueWithTypeMatch: "string"
valueWithMin:
- 1
- 2
- 3
valueWithMax:
- 1
- 2
- 3
valueWithMinMax:
- 1
- 2
- 3
valueWithMinEmpty: []
valueWithMaxEmpty: []
key:
'complex.key' : 'foo'
nulValue: null
matchers:
headers:
- key: Content-Type
regex: "application/json.*"
cookies:
- key: foo
regex: '[0-9]'
- key: bar
command: 'equals($it)'
body:
- path: $.duck
type: by_regex
value: "[0-9]{3}"
- path: $.duck
type: by_equality
- path: $.alpha
type: by_regex
predefined: only_alpha_unicode
- path: $.alpha
type: by_equality
- path: $.number
type: by_regex
predefined: number
- path: $.aBoolean
type: by_regex
predefined: any_boolean
- path: $.date
type: by_date
- path: $.dateTime
type: by_timestamp
- path: $.time
type: by_time
- path: $.valueWithTypeMatch
type: by_type
- path: $.valueWithMin
type: by_type
minOccurrence: 1
- path: $.valueWithMax
type: by_type
maxOccurrence: 3
- path: $.valueWithMinMax
type: by_type
minOccurrence: 1
maxOccurrence: 3
- path: $.valueWithMinEmpty
type: by_type
minOccurrence: 0
- path: $.valueWithMaxEmpty
type: by_type
maxOccurrence: 0
- path: $.duck
type: by_command
value: assertThatValueIsANumber($it)
- path: $.nullValue
type: by_null
value: null
headers:
Content-Type: application/json</pre><p>
</p><p>In the preceding example, you can see the dynamic portions of the contract in the
<code class="literal">matchers</code> sections. For the request part, you can see that, for all fields but
<code class="literal">valueWithoutAMatcher</code>, the values of the regular expressions that the stub should
contain are explicitly set. For the <code class="literal">valueWithoutAMatcher</code>, the verification takes place
in the same way as without the use of matchers. In that case, the test performs an
equality check.</p><p>For the response side in the <code class="literal">bodyMatchers</code> section, we define the dynamic parts in a
similar manner. The only difference is that the <code class="literal">byType</code> matchers are also present. The
verifier engine checks four fields to verify whether the response from the test
has a value for which the JSON path matches the given field, is of the same type as the one
defined in the response body, and passes the following check (based on the method being called):</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">For <code class="literal">$.valueWithTypeMatch</code>, the engine checks whether the type is the same.</li><li class="listitem">For <code class="literal">$.valueWithMin</code>, the engine check the type and asserts whether the size is greater
than or equal to the minimum occurrence.</li><li class="listitem">For <code class="literal">$.valueWithMax</code>, the engine checks the type and asserts whether the size is
smaller than or equal to the maximum occurrence.</li><li class="listitem">For <code class="literal">$.valueWithMinMax</code>, the engine checks the type and asserts whether the size is
between the min and maximum occurrence.</li></ul></div><p>The resulting test would resemble the following example (note that an <code class="literal">and</code> section
separates the autogenerated assertions and the assertion from matchers):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"duck\":123,\"alpha\":\"abc\",\"number\":123,\"aBoolean\":true,\"date\":\"2017-01-01\",\"dateTime\":\"2017-01-01T01:23:45\",\"time\":\"01:02:34\",\"valueWithoutAMatcher\":\"foo\",\"valueWithTypeMatch\":\"string\",\"key\":{\"complex.key\":\"foo\"}}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/get"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json.*"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['valueWithoutAMatcher']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.duck"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{3}"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.duck"</span>, Integer.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).isEqualTo(<span class="hl-number">123</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.alpha"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[\\p{L}]*"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.alpha"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"abc"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.number"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"-?(\\d*\\.\\d+|\\d+)"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.aBoolean"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(true|false)"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.date"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.dateTime"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"</span>);
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.time"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])"</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithTypeMatch"</span>)).isInstanceOf(java.lang.String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMin"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMin"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).as(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMin"</span>).hasSizeGreaterThanOrEqualTo(<span class="hl-number">1</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMax"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMax"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).as(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMax"</span>).hasSizeLessThanOrEqualTo(<span class="hl-number">3</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinMax"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinMax"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).as(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinMax"</span>).hasSizeBetween(<span class="hl-number">1</span>, <span class="hl-number">3</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinEmpty"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinEmpty"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).as(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMinEmpty"</span>).hasSizeGreaterThanOrEqualTo(<span class="hl-number">0</span>);
assertThat((Object) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMaxEmpty"</span>)).isInstanceOf(java.util.List.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
assertThat((java.lang.Iterable) parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMaxEmpty"</span>, java.util.Collection.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).as(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.valueWithMaxEmpty"</span>).hasSizeLessThanOrEqualTo(<span class="hl-number">0</span>);
assertThatValueIsANumber(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.duck"</span>));
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.['key'].['complex.key']"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>);</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Notice that, for the <code class="literal">byCommand</code> method, the example calls the
<code class="literal">assertThatValueIsANumber</code>. This method must be defined in the test base class or be
statically imported to your tests. Notice that the <code class="literal">byCommand</code> call was converted to
<code class="literal">assertThatValueIsANumber(parsedJson.read("$.duck"));</code>. That means that the engine took
the method name and passed the proper JSON path as a parameter to it.</p></td></tr></table></div><p>The resulting WireMock stub is in the following example:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"urlPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/get"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json.*"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['valueWithoutAMatcher'] == 'foo')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['valueWithTypeMatch'] == 'string')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.['list'].['some'].['nested'][?(@.['anothervalue'] == 4)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.['list'].['someother'].['nested'][?(@.['anothervalue'] == 4)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.['list'].['someother'].['nested'][?(@.['json'] == 'with value')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.duck =~ /([0-9]{3})/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.duck == 123)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.alpha =~ /([\\\\p{L}]*)/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.alpha == 'abc')]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.number =~ /(-?(\\\\d*\\\\.\\\\d+|\\\\d+))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.aBoolean =~ /((true|false))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.date =~ /((\\\\d\\\\d\\\\d\\\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01]))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.dateTime =~ /(([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.time =~ /((2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9]))/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.list.some.nested[?(@.json =~ /(.*)/)]"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\\"</span>date\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span><span class="hl-number">2017</span>-<span class="hl-number">01</span>-<span class="hl-number">01</span>\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>dateTime\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span><span class="hl-number">2017</span>-<span class="hl-number">01</span>-<span class="hl-number">01</span>T01:<span class="hl-number">23</span>:<span class="hl-number">45</span>\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>number\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":123,\\"</span>aBoolean\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":true,\\"</span>duck\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":123,\\"</span>alpha\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>abc\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>valueWithMin\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":[1,2,3],\\"</span>time\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span><span class="hl-number">01</span>:<span class="hl-number">02</span>:<span class="hl-number">34</span>\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>valueWithTypeMatch\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>string\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">",\\"</span>valueWithMax\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":[1,2,3],\\"</span>valueWithMinMax\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":[1,2,3],\\"</span>valueWithoutAMatcher\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">":\\"</span>foo\\<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"}"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you use a <code class="literal">matcher</code>, then the part of the request and response that the
<code class="literal">matcher</code> addresses with the JSON Path gets removed from the assertion. In the case of
verifying a collection, you must create matchers for <span class="strong"><strong>all</strong></span> the elements of the
collection.</p></td></tr></table></div><p>Consider the following example:</p><pre class="programlisting">Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>)
}
response {
status OK()
body(events: [[
operation : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'EXPORT'</span>,
eventId : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'16f1ed75-0bcc-4f0d-a04d-3121798faf99'</span>,
status : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'OK'</span>
], [
operation : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'INPUT_PROCESSING'</span>,
eventId : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'3bb4ac82-6652-462f-b6d1-75e424a0024a'</span>,
status : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'OK'</span>
]
]
)
bodyMatchers {
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.events[0].operation'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'.+'</span>))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.events[0].eventId'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})$'</span>))
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$.events[0].status'</span>, byRegex(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'.+'</span>))
}
}
}</pre><p>The preceding code leads to creating the following test (the code block shows only the assertion section):</p><pre class="programlisting">and:
DocumentContext parsedJson = JsonPath.parse(response.body.asString())
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['eventId']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"16f1ed75-0bcc-4f0d-a04d-3121798faf99"</span>)
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['operation']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"EXPORT"</span>)
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['operation']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"INPUT_PROCESSING"</span>)
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['eventId']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"3bb4ac82-6652-462f-b6d1-75e424a0024a"</span>)
assertThatJson(parsedJson).array(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['events']"</span>).contains(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['status']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>)
and:
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.events[0].operation"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".+"</span>)
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.events[0].eventId"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"^([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})\$"</span>)
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"\$.events[0].status"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".+"</span>)</pre><p>As you can see, the assertion is malformed. Only the first element of the array got
asserted. In order to fix this, you should apply the assertion to the whole <code class="literal">$.events</code>
collection and assert it with the <code class="literal">byCommand(&#8230;&#8203;)</code> method.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_jax_rs_support" href="#_jax_rs_support"></a>92.6&nbsp;JAX-RS Support</h2></div></div></div><p>The Spring Cloud Contract Verifier supports the JAX-RS 2 Client API. The base class needs
to define <code class="literal">protected WebTarget webTarget</code> and server initialization. The only option for
testing JAX-RS API is to start a web server. Also, a request with a body needs to have a
content type set. Otherwise, the default of <code class="literal">application/octet-stream</code> gets used.</p><p>In order to use JAX-RS mode, use the following settings:</p><pre class="programlisting">testMode == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'JAXRSCLIENT'</span></pre><p>The following example shows a generated test API:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
Response response = webTarget
.path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"limit"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"10"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"offset"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"20"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"filter"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"email"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"sort"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"search"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"55"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"age"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"99"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Denis.Stepanov"</span>)
.queryParam(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"email"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bob@email.com"</span>)
.request()
.method(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"GET"</span>);
String responseAsString = response.readEntity(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.getStatus()).isEqualTo(<span class="hl-number">200</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(responseAsString);
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['property1']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_async_support" href="#_async_support"></a>92.7&nbsp;Async Support</h2></div></div></div><p>If you&#8217;re using asynchronous communication on the server side (your controllers are
returning <code class="literal">Callable</code>, <code class="literal">DeferredResult</code>, and so on), then, inside your contract, you must
provide an <code class="literal">async()</code> method in the <code class="literal">response</code> section. The following code shows an example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/get'</span>
}
response {
status OK()
body <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Passed'</span>
async()
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">response:
async: true</pre><p>
</p><p>You can also use the <code class="literal">fixedDelayMilliseconds</code> method / property to add delay to your stubs.</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method GET()
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/get'</span>
}
response {
status <span class="hl-number">200</span>
body <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Passed'</span>
fixedDelayMilliseconds <span class="hl-number">1000</span>
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">response:
fixedDelayMilliseconds: 1000</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_working_with_context_paths" href="#_working_with_context_paths"></a>92.8&nbsp;Working with Context Paths</h2></div></div></div><p>Spring Cloud Contract supports context paths.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The only change needed to fully support context paths is the switch on the
<span class="strong"><strong>PRODUCER</strong></span> side. Also, the autogenerated tests must use <span class="strong"><strong>EXPLICIT</strong></span> mode. The consumer
side remains untouched. In order for the generated test to pass, you must use <span class="strong"><strong>EXPLICIT</strong></span>
mode.</p></td></tr></table></div><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testMode&gt;</span>EXPLICIT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testMode&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">contracts {
testMode = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'EXPLICIT'</span>
}</pre><p class="secondary">
</p><p>That way, you generate a test that <span class="strong"><strong>DOES NOT</strong></span> use MockMvc. It means that you generate
real requests and you need to setup your generated test&#8217;s base class to work on a real
socket.</p><p>Consider the following contract:</p><pre class="programlisting">org.springframework.cloud.contract.spec.Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'GET'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/my-context-path/url'</span>
}
response {
status OK()
}
}</pre><p>The following example shows how to set up a base class and Rest Assured:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> io.restassured.RestAssured;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Before;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.boot.web.server.LocalServerPort;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.boot.test.context.SpringBootTest;
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(classes = ContextPathTestingBaseClass.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ContextPathTestingBaseClass {
<em><span class="hl-annotation" style="color: gray">@LocalServerPort</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port;
<em><span class="hl-annotation" style="color: gray">@Before</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setup() {
RestAssured.baseURI = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://localhost"</span>;
RestAssured.port = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.port;
}
}</pre><p>If you do it this way:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">All of your requests in the autogenerated tests are sent to the real endpoint with your
context path included (for example, <code class="literal">/my-context-path/url</code>).</li><li class="listitem">Your contracts reflect that you have a context path. Your generated stubs also have
that information (for example, in the stubs, you have to call <code class="literal">/my-context-path/url</code>).</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_working_with_web_flux" href="#_working_with_web_flux"></a>92.9&nbsp;Working with Web Flux</h2></div></div></div><p>Spring Cloud Contract requires the usage of <code class="literal">EXPLICIT</code> mode in your generated tests
to work with Web Flux.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;testMode&gt;</span>EXPLICIT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/testMode&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">contracts {
testMode = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'EXPLICIT'</span>
}</pre><p class="secondary">
</p><p>The following example shows how to set up a base class and Rest Assured for Web Flux:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(classes = BeerRestBase.Config.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = "server.port=0")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BeerRestBase {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// your tests go here</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// in this config class you define all controllers and mocked services</span>
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableAutoConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
PersonCheckingService personCheckingService() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> personToCheck -&gt; personToCheck.age &gt;= <span class="hl-number">20</span>;
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
ProducerController producerController() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ProducerController(personCheckingService());
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_messaging_top_level_elements" href="#_messaging_top_level_elements"></a>92.10&nbsp;Messaging Top-Level Elements</h2></div></div></div><p>The DSL for messaging looks a little bit different than the one that focuses on HTTP. The
following sections explain the differences:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#contract-dsl-output-triggered-method" title="92.10.1&nbsp;Output Triggered by a Method">Section&nbsp;92.10.1, &#8220;Output Triggered by a Method&#8221;</a></li><li class="listitem"><a class="xref" href="#contract-dsl-output-triggered-message" title="92.10.2&nbsp;Output Triggered by a Message">Section&nbsp;92.10.2, &#8220;Output Triggered by a Message&#8221;</a></li><li class="listitem"><a class="xref" href="#contract-dsl-consumer-producer" title="92.10.3&nbsp;Consumer/Producer">Section&nbsp;92.10.3, &#8220;Consumer/Producer&#8221;</a></li><li class="listitem"><a class="xref" href="#contract-dsl-common" title="92.10.4&nbsp;Common">Section&nbsp;92.10.4, &#8220;Common&#8221;</a></li></ul></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-output-triggered-method" href="#contract-dsl-output-triggered-method"></a>92.10.1&nbsp;Output Triggered by a Method</h3></div></div></div><p>The output message can be triggered by calling a method (such as a <code class="literal">Scheduler</code> when a was
started and a message was sent), as shown in the following example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">def dsl = Contract.make {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Human readable description</span>
description <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Some description'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Label by means of which the output message can be triggered</span>
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// input to the contract</span>
input {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the contract will be triggered by a method</span>
triggeredBy(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'bookReturnedTriggered()'</span>)
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// output message of the contract</span>
outputMessage {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// destination to which the output message will be sent</span>
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'output'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the body of the output message</span>
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'{ "bookName" : "foo" }'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the headers of the output message</span>
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting"># Human readable description
description: Some description
# Label by means of which the output message can be triggered
label: some_label
input:
# the contract will be triggered by a method
triggeredBy: bookReturnedTriggered()
# output message of the contract
outputMessage:
# destination to which the output message will be sent
sentTo: output
# the body of the output message
body:
bookName: foo
# the headers of the output message
headers:
BOOK-NAME: foo</pre><p>
</p><p>In the previous example case, the output message is sent to <code class="literal">output</code> if a method called
<code class="literal">bookReturnedTriggered</code> is executed. On the message <span class="strong"><strong>publisher&#8217;s</strong></span> side, we generate a
test that calls that method to trigger the message. On the <span class="strong"><strong>consumer</strong></span> side, you can use
the <code class="literal">some_label</code> to trigger the message.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-output-triggered-message" href="#contract-dsl-output-triggered-message"></a>92.10.2&nbsp;Output Triggered by a Message</h3></div></div></div><p>The output message can be triggered by receiving a message, as shown in the following
example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting">def dsl = Contract.make {
description <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Some Description'</span>
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// input is a message</span>
input {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// the message was received from this destination</span>
messageFrom(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'input'</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// has the following body</span>
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and the following headers</span>
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'output'</span>)
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'BOOK-NAME'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>)
}
}
}</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting"># Human readable description
description: Some description
# Label by means of which the output message can be triggered
label: some_label
# input is a message
input:
messageFrom: input
# has the following body
messageBody:
bookName: 'foo'
# and the following headers
messageHeaders:
sample: 'header'
# output message of the contract
outputMessage:
# destination to which the output message will be sent
sentTo: output
# the body of the output message
body:
bookName: foo
# the headers of the output message
headers:
BOOK-NAME: foo</pre><p>
</p><p>In the preceding example, the output message is sent to <code class="literal">output</code> if a proper message is
received on the <code class="literal">input</code> destination. On the message <span class="strong"><strong>publisher&#8217;s</strong></span> side, the engine
generates a test that sends the input message to the defined destination. On the
<span class="strong"><strong>consumer</strong></span> side, you can either send a message to the input destination or use a label
(<code class="literal">some_label</code> in the example) to trigger the message.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-consumer-producer" href="#contract-dsl-consumer-producer"></a>92.10.3&nbsp;Consumer/Producer</h3></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is valid only for Groovy DSL.</p></td></tr></table></div><p>In HTTP, you have a notion of <code class="literal">client</code>/<code class="literal">stub and `server</code>/<code class="literal">test</code> notation. You can also
use those paradigms in messaging. In addition, Spring Cloud Contract Verifier also
provides the <code class="literal">consumer</code> and <code class="literal">producer</code> methods, as presented in the following example
(note that you can use either <code class="literal">$</code> or <code class="literal">value</code> methods to provide <code class="literal">consumer</code> and <code class="literal">producer</code>
parts):</p><pre class="programlisting">Contract.make {
label <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'some_label'</span>
input {
messageFrom value(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>))
messageBody([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
messageHeaders {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'sample'</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'header'</span>)
}
}
outputMessage {
sentTo $(consumer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:input'</span>), producer(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'jms:output'</span>))
body([
bookName: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'foo'</span>
])
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="contract-dsl-common" href="#contract-dsl-common"></a>92.10.4&nbsp;Common</h3></div></div></div><p>In the <code class="literal">input</code> or <code class="literal">outputMessage</code> section you can call <code class="literal">assertThat</code> with the name
of a <code class="literal">method</code> (e.g. <code class="literal">assertThatMessageIsOnTheQueue()</code>) that you have defined in the
base class or in a static import. Spring Cloud Contract will execute that method
in the generated test.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_multiple_contracts_in_one_file" href="#_multiple_contracts_in_one_file"></a>92.11&nbsp;Multiple Contracts in One File</h2></div></div></div><p>You can define multiple contracts in one file. Such a contract might resemble the
following example:</p><p><b>Groovy DSL.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
[
Contract.make {
name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"should post a user"</span>)
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users/1'</span>)
}
response {
status OK()
}
},
Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/users/2'</span>)
}
response {
status OK()
}
}
]</pre><p>
</p><p><b>YAML.&nbsp;</b>
</p><pre class="programlisting">---
name: should post a user
request:
method: POST
url: /users/1
response:
status: 200
---
request:
method: POST
url: /users/2
response:
status: 200</pre><p>
</p><p>In the preceding example, one contract has the <code class="literal">name</code> field and the other does not. This
leads to generation of two tests that look more or less like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.verifier.tests.com.hello;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.example.TestBase;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.jsonpath.DocumentContext;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.jsonpath.JsonPath;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.restassured.module.mockmvc.specification.MockMvcRequestSpecification;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.jayway.restassured.response.ResponseOptions;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Test;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> com.jayway.restassured.module.mockmvc.RestAssuredMockMvc.*;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> com.toomuchcoding.jsonassert.JsonAssertion.assertThatJson;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> org.assertj.core.api.Assertions.assertThat;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> V1Test <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> TestBase {
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_should_post_a_user() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users/1"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
}
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_withList_<span class="hl-number">1</span>() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/users/2"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
}
}</pre><p>Notice that, for the contract that has the <code class="literal">name</code> field, the generated test method is named
<code class="literal">validate_should_post_a_user</code>. For the one that does not have the name, it is called
<code class="literal">validate_withList_1</code>. It corresponds to the name of the file <code class="literal">WithList.groovy</code> and the
index of the contract in the list.</p><p>The generated stubs is shown in the following example:</p><pre class="screen">should post a user.json
1_WithList.json</pre><p>As you can see, the first file got the <code class="literal">name</code> parameter from the contract. The second
got the name of the contract file (<code class="literal">WithList.groovy</code>) prefixed with the index (in this
case, the contract had an index of <code class="literal">1</code> in the list of contracts in the file).</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>As you can see, it is much better if you name your contracts because doing so makes
your tests far more meaningful.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_generating_spring_rest_docs_snippets_from_the_contracts" href="#_generating_spring_rest_docs_snippets_from_the_contracts"></a>92.12&nbsp;Generating Spring REST Docs snippets from the contracts</h2></div></div></div><p>When you want to include the requests and responses of your API using Spring REST Docs,
you only need to make some minor changes to your setup if you are using MockMvc and RestAssuredMockMvc.
Simply include the following dependencies if you haven&#8217;t already.</p><p><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-contract-verifier<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.restdocs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-restdocs-mockmvc<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;optional&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/optional&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>
</p><p><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.cloud:spring-cloud-starter-contract-verifier'</span>
testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'org.springframework.restdocs:spring-restdocs-mockmvc'</span></pre><p>
</p><p>Next you need to make some changes to your base class like the following example.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example.fraud;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> io.restassured.module.mockmvc.RestAssuredMockMvc;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Before;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Rule;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.rules.TestName;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.runner.RunWith;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.boot.test.context.SpringBootTest;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.restdocs.JUnitRestDocumentation;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.test.context.junit4.SpringRunner;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.test.web.servlet.setup.MockMvcBuilders;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.web.context.WebApplicationContext;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
<em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(classes = Application.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FraudBaseWithWebAppSetup {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String OUTPUT = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/generated-snippets"</span>;
<em><span class="hl-annotation" style="color: gray">@Rule</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> JUnitRestDocumentation restDocumentation = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> JUnitRestDocumentation(OUTPUT);
<em><span class="hl-annotation" style="color: gray">@Rule</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> TestName testName = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TestName();
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> WebApplicationContext context;
<em><span class="hl-annotation" style="color: gray">@Before</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setup() {
RestAssuredMockMvc.mockMvc(MockMvcBuilders.webAppContextSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.context)
.apply(documentationConfiguration(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.restDocumentation))
.alwaysDo(document(getClass().getSimpleName() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"_"</span> + testName.getMethodName()))
.build());
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">protected</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> assertThatRejectionReasonIsNull(Object rejectionReason) {
assert rejectionReason == null;
}
}</pre><p>In case you are using the standalone setup, you can set up RestAssuredMockMvc like this:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example.fraud;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> io.restassured.module.mockmvc.RestAssuredMockMvc;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Before;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.Rule;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.junit.rules.TestName;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.restdocs.JUnitRestDocumentation;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.test.web.servlet.setup.MockMvcBuilders;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.documentationConfiguration;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">abstract</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FraudBaseWithStandaloneSetup {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String OUTPUT = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"target/generated-snippets"</span>;
<em><span class="hl-annotation" style="color: gray">@Rule</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> JUnitRestDocumentation restDocumentation = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> JUnitRestDocumentation(OUTPUT);
<em><span class="hl-annotation" style="color: gray">@Rule</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> TestName testName = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TestName();
<em><span class="hl-annotation" style="color: gray">@Before</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> setup() {
RestAssuredMockMvc.standaloneSetup(MockMvcBuilders.standaloneSetup(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FraudDetectionController())
.apply(documentationConfiguration(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.restDocumentation))
.alwaysDo(document(getClass().getSimpleName() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"_"</span> + testName.getMethodName())));
}
}</pre><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You don&#8217;t need to specify the output directory for the generated snippets since version 1.2.0.RELEASE of Spring REST Docs.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_customization" href="#_customization"></a>93.&nbsp;Customization</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is valid only for Groovy DSL</p></td></tr></table></div><p>You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in
the remainder of this section.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_extending_the_dsl" href="#_extending_the_dsl"></a>93.1&nbsp;Extending the DSL</h2></div></div></div><p>You can provide your own functions to the DSL. The key requirement for this feature is to
maintain the static compatibility. Later in this document, you can see examples of:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Creating a JAR with reusable classes.</li><li class="listitem">Referencing of these classes in the DSLs.</li></ul></div><p>You can find the full example
<a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples" target="_top">here</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_common_jar" href="#_common_jar"></a>93.1.1&nbsp;Common JAR</h3></div></div></div><p>The following examples show three classes that can be reused in the DSLs.</p><p><span class="strong"><strong>PatternUtils</strong></span> contains functions used by both the <span class="strong"><strong>consumer</strong></span> and the <span class="strong"><strong>producer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.regex.Pattern;
<strong class="hl-tag" style="color: blue">/**
* If you want to use {@link Pattern} directly in your tests
* then you can create a class resembling this one. It can
* contain all the {@link Pattern} you want to use in the DSL.
*
* &lt;pre&gt;
* {@code
* request {
* body(
* [ age: $(c(PatternUtils.oldEnough()))]
* )
* }
* &lt;/pre&gt;
*
* Notice that we're using both {@code $()} for dynamic values
* and {@code c()} for the consumer side.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PatternUtils {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> String tooYoung() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-1][0-9]"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> Pattern oldEnough() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[2-9][0-9]"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
<strong class="hl-tag" style="color: blue">/**
* Makes little sense but it's just an example ;)
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> Pattern ok() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre><p><span class="strong"><strong>ConsumerUtils</strong></span> contains functions used by the <span class="strong"><strong>consumer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.internal.ClientDslProperty;
<strong class="hl-tag" style="color: blue">/**
* DSL Properties passed to the DSL from the consumer's perspective.
* That means that on the input side {@code Request} for HTTP
* or {@code Input} for messaging you can have a regular expression.
* On the {@code Response} for HTTP or {@code Output} for messaging
* you have to have a concrete value.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ConsumerUtils {
<strong class="hl-tag" style="color: blue">/**
* Consumer side property. By using the {@link ClientDslProperty}
* you can omit most of boilerplate code from the perspective
* of dynamic values. Example
*
* &lt;pre&gt;
* {@code
* request {
* body(
* [ age: $(ConsumerUtils.oldEnough())]
* )
* }
* &lt;/pre&gt;
*
* That way it's in the implementation that we decide what value we will pass to the consumer
* and which one to the producer.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> ClientDslProperty oldEnough() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// this example is not the best one and</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// theoretically you could just pass the regex instead of `ServerDslProperty` but</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// it's just to show some new tricks :)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ClientDslProperty(PatternUtils.oldEnough(), <span class="hl-number">40</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre><p><span class="strong"><strong>ProducerUtils</strong></span> contains functions used by the <span class="strong"><strong>producer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.internal.ServerDslProperty;
<strong class="hl-tag" style="color: blue">/**
* DSL Properties passed to the DSL from the producer's perspective.
* That means that on the input side {@code Request} for HTTP
* or {@code Input} for messaging you have to have a concrete value.
* On the {@code Response} for HTTP or {@code Output} for messaging
* you can have a regular expression.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ProducerUtils {
<strong class="hl-tag" style="color: blue">/**
* Producer side property. By using the {@link ProducerUtils}
* you can omit most of boilerplate code from the perspective
* of dynamic values. Example
*
* &lt;pre&gt;
* {@code
* response {
* body(
* [ status: $(ProducerUtils.ok())]
* )
* }
* &lt;/pre&gt;
*
* That way it's in the implementation that we decide what value we will pass to the consumer
* and which one to the producer.
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> ServerDslProperty ok() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// this example is not the best one and</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// theoretically you could just pass the regex instead of `ServerDslProperty` but</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// it's just to show some new tricks :)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ServerDslProperty( PatternUtils.ok(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>);
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_the_dependency_to_the_project" href="#_adding_the_dependency_to_the_project"></a>93.1.2&nbsp;Adding the Dependency to the Project</h3></div></div></div><p>In order for the plugins and IDE to be able to reference the common JAR classes, you need
to pass the dependency to your project.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_test_the_dependency_in_the_project_s_dependencies" href="#_test_the_dependency_in_the_project_s_dependencies"></a>93.1.3&nbsp;Test the Dependency in the Project&#8217;s Dependencies</h3></div></div></div><p>First, add the common jar dependency as a test dependency. Because your contracts files
are available on the test resources path, the common jar classes automatically become
visible in your Groovy files. The following examples show how to test the dependency:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>beer-common<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${project.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-common:0.0.1-SNAPSHOT"</span>)</pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_test_a_dependency_in_the_plugin_s_dependencies" href="#_test_a_dependency_in_the_plugin_s_dependencies"></a>93.1.4&nbsp;Test a Dependency in the Plugin&#8217;s Dependencies</h3></div></div></div><p>Now, you must add the dependency for the plugin to reuse at runtime, as shown in the
following example:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractPackageRegex&gt;</span>.*intoxication.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractPackageRegex&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassFQN&gt;</span>com.example.intoxication.BeerIntoxicationBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassFQN&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>beer-common<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${project.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>compile<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-common:0.0.1-SNAPSHOT"</span></pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_referencing_classes_in_dsls" href="#_referencing_classes_in_dsls"></a>93.1.5&nbsp;Referencing classes in DSLs</h3></div></div></div><p>You can now reference your classes in your DSL, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> contracts.beer.rest
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.example.ConsumerUtils
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.example.ProducerUtils
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
Contract.make {
description(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"
</span>Represents a successful scenario of getting a beer
```
given:
client is old enough
when:
he applies <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> a beer
then:
we<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'ll grant him the beer
</span>```
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">")
</span> request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/check'</span>
body(
age: $(ConsumerUtils.oldEnough())
)
headers {
contentType(applicationJson())
}
}
response {
status <span class="hl-number">200</span>
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"
</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${value(ProducerUtils.ok())}"</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">")
</span> headers {
contentType(applicationJson())
}
}
}</pre></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_using_the_pluggable_architecture" href="#_using_the_pluggable_architecture"></a>94.&nbsp;Using the Pluggable Architecture</h2></div></div></div><p>You may encounter cases where you have your contracts have been defined in other formats,
such as YAML, RAML or PACT. In those cases, you still want to benefit from the automatic
generation of tests and stubs. You can add your own implementation for generating both
tests and stubs. Also, you can customize the way tests are generated (for example, you
can generate tests for other languages) and the way stubs are generated (for example, you
can generate stubs for other HTTP server implementations).</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_custom_contract_converter" href="#_custom_contract_converter"></a>94.1&nbsp;Custom Contract Converter</h2></div></div></div><p>The <code class="literal">ContractConverter</code> interface lets you register your own implementation of a contract
structure converter. The following code listing shows the <code class="literal">ContractConverter</code> interface:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.spec
<strong class="hl-tag" style="color: blue">/**
* Converter to be used to convert FROM {@link File} TO {@link Contract}
* and from {@link Contract} to {@code T}
*
* @param &lt;T&gt; - type to which we want to convert the contract
*
* @author Marcin Grzejszczak
* @since 1.1.0
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> ContractConverter&lt;T&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> ContractStorer&lt;T&gt; {
<strong class="hl-tag" style="color: blue">/**
* Should this file be accepted by the converter. Can use the file extension
* to check if the conversion is possible.
*
* @param file - file to be considered for conversion
* @return - {@code true} if the given implementation can convert the file
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isAccepted(File file)
<strong class="hl-tag" style="color: blue">/**
* Converts the given {@link File} to its {@link Contract} representation
*
* @param file - file to convert
* @return - {@link Contract} representation of the file
*/</strong>
Collection&lt;Contract&gt; convertFrom(File file)
<strong class="hl-tag" style="color: blue">/**
* Converts the given {@link Contract} to a {@link T} representation
*
* @param contract - the parsed contract
* @return - {@link T} the type to which we do the conversion
*/</strong>
T convertTo(Collection&lt;Contract&gt; contract)
}</pre><p>Your implementation must define the condition on which it should start the
conversion. Also, you must define how to perform that conversion in both directions.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Once you create your implementation, you must create a
<code class="literal">/META-INF/spring.factories</code> file in which you provide the fully qualified name of your
implementation.</p></td></tr></table></div><p>The following example shows a typical <code class="literal">spring.factories</code> file:</p><pre class="screen">org.springframework.cloud.contract.spec.ContractConverter=\
org.springframework.cloud.contract.verifier.converter.YamlContractConverter</pre><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="pact-converter" href="#pact-converter"></a>94.1.1&nbsp;Pact Converter</h3></div></div></div><p>Spring Cloud Contract includes support for <a class="link" href="https://docs.pact.io/" target="_top">Pact</a> representation of
contracts up until v4. Instead of using the Groovy DSL, you can use Pact files. In this section, we
present how to add Pact support for your project. Note however that not all functionality is supported.
Starting with v3 you can combine multiple matcher for the same element;
you can use matchers for the body, headers, request and path; and you can use value generators.
Spring Cloud Contract currently only supports multiple matchers that are combined using the AND rule logic.
Next to that the request and path matchers are skipped during the conversion.
When using a date, time or datetime value generator with a given format,
the given format will be skipped and the ISO format will be used.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_pact_contract" href="#_pact_contract"></a>94.1.2&nbsp;Pact Contract</h3></div></div></div><p>Consider following example of a Pact contract, which is a file under the
<code class="literal">src/test/resources/contracts</code> folder.</p><pre class="programlisting">{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"provider"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Provider"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"consumer"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Consumer"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"interactions"</span>: [
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"description"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PUT"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"path"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"clientId"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"1234567890"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"loanAmount"</span>: <span class="hl-number">99999</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"generators"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.clientId"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Regex"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{10}"</span>
}
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchingRules"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"header"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchers"</span>: [
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"match"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd\\.fraud\\.v1\\+json.*"</span>
}
],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"combine"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"AND"</span>
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.clientId"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchers"</span>: [
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"match"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-9]{10}"</span>
}
],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"combine"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"AND"</span>
}
}
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span class="hl-number">200</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json;charset=UTF-8"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"fraudCheckStatus"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"rejectionReason"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchingRules"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"header"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchers"</span>: [
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"match"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd\\.fraud\\.v1\\+json.*"</span>
}
],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"combine"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"AND"</span>
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.fraudCheckStatus"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchers"</span>: [
{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"match"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"regex"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>
}
],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"combine"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"AND"</span>
}
}
}
}
}
],
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"metadata"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"pact-specification"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"version"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"3.0.0"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"pact-jvm"</span>: {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"version"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"3.5.13"</span>
}
}
}</pre><p>The remainder of this section about using Pact refers to the preceding file.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_pact_for_producers" href="#_pact_for_producers"></a>94.1.3&nbsp;Pact for Producers</h3></div></div></div><p>On the producer side, you must add two additional dependencies to your plugin
configuration. One is the Spring Cloud Contract Pact support, and the other represents
the current Pact version that you use.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example.fraud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-pact<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-pact:${findProperty('verifierVersion') ?: verifierVersion}"</span></pre><p class="secondary">
</p><p>When you execute the build of your application, a test will be generated. The generated
test might be as follows:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> validate_shouldMarkClientAsFraud() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// given:</span>
MockMvcRequestSpecification request = given()
.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json"</span>)
.body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"clientId\":\"1234567890\",\"loanAmount\":99999}"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when:</span>
ResponseOptions response = given().spec(request)
.put(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then:</span>
assertThat(response.statusCode()).isEqualTo(<span class="hl-number">200</span>);
assertThat(response.header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd\\.fraud\\.v1\\+json.*"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
DocumentContext parsedJson = JsonPath.parse(response.getBody().asString());
assertThatJson(parsedJson).field(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"['rejectionReason']"</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Amount too high"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// and:</span>
assertThat(parsedJson.read(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.fraudCheckStatus"</span>, String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)).matches(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FRAUD"</span>);
}</pre><p>The corresponding generated stub might be as follows:</p><pre class="programlisting">{
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"id"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"996ae5ae-6834-4db6-8fac-358ca187ab62"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"uuid"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"996ae5ae-6834-4db6-8fac-358ca187ab62"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/fraudcheck"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"PUT"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matches"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd\\.fraud\\.v1\\+json.*"</span>
}
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : [ {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.['loanAmount'] == 99999)]"</span>
}, {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.clientId =~ /([0-9]{10})/)]"</span>
} ]
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/vnd.fraud.v1+json;charset=UTF-8"</span>
},
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"transformers"</span> : [ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response-template"</span> ]
},
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_pact_for_consumers" href="#_pact_for_consumers"></a>94.1.4&nbsp;Pact for Consumers</h3></div></div></div><p>On the producer side, you must add two additional dependencies to your project
dependencies. One is the Spring Cloud Contract Pact support, and the other represents the
current Pact version that you use.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-pact<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"org.springframework.cloud:spring-cloud-contract-pact"</span></pre><p class="secondary">
</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_the_custom_test_generator" href="#_using_the_custom_test_generator"></a>94.2&nbsp;Using the Custom Test Generator</h2></div></div></div><p>If you want to generate tests for languages other than Java or you are not happy with the
way the verifier builds Java tests, you can register your own implementation.</p><p>The <code class="literal">SingleTestGenerator</code> interface lets you register your own implementation. The
following code listing shows the <code class="literal">SingleTestGenerator</code> interface:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.verifier.builder
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.verifier.file.ContractMetadata
<strong class="hl-tag" style="color: blue">/**
* Builds a single test.
*
* @since 1.1.0
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> SingleTestGenerator {
<strong class="hl-tag" style="color: blue">/**
* Creates contents of a single test class in which all test scenarios from
* the contract metadata should be placed.
*
* @param properties - properties passed to the plugin
* @param listOfFiles - list of parsed contracts with additional metadata
* @param className - the name of the generated test class
* @param classPackage - the name of the package in which the test class should be stored
* @param includedDirectoryRelativePath - relative path to the included directory
* @return contents of a single test class
*/</strong>
String buildClass(ContractVerifierConfigProperties properties, Collection&lt;ContractMetadata&gt; listOfFiles,
String className, String classPackage, String includedDirectoryRelativePath)
<strong class="hl-tag" style="color: blue">/**
* Extension that should be appended to the generated test class. E.g. {@code .java} or {@code .php}
*
* @param properties - properties passed to the plugin
*/</strong>
String fileExtension(ContractVerifierConfigProperties properties)
}</pre><p>Again, you must provide a <code class="literal">spring.factories</code> file, such as the one shown in the following
example:</p><pre class="screen">org.springframework.cloud.contract.verifier.builder.SingleTestGenerator=/
com.example.MyGenerator</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_the_custom_stub_generator" href="#_using_the_custom_stub_generator"></a>94.3&nbsp;Using the Custom Stub Generator</h2></div></div></div><p>If you want to generate stubs for stub servers other than WireMock, you can plug in your
own implementation of the <code class="literal">StubGenerator</code> interface. The following code listing shows the
<code class="literal">StubGenerator</code> interface:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.verifier.converter
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> groovy.transform.CompileStatic
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.verifier.file.ContractMetadata
<strong class="hl-tag" style="color: blue">/**
* Converts contracts into their stub representation.
*
* @since 1.1.0
*/</strong>
<em><span class="hl-annotation" style="color: gray">@CompileStatic</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> StubGenerator {
<strong class="hl-tag" style="color: blue">/**
* Returns {@code true} if the converter can handle the file to convert it into a stub.
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> canHandleFileName(String fileName)
<strong class="hl-tag" style="color: blue">/**
* Returns the collection of converted contracts into stubs. One contract can
* result in multiple stubs.
*/</strong>
Map&lt;Contract, String&gt; convertContents(String rootName, ContractMetadata content)
<strong class="hl-tag" style="color: blue">/**
* Returns the name of the converted stub file. If you have multiple contracts
* in a single file then a prefix will be added to the generated file. If you
* provide the {@link Contract#name} field then that field will override the
* generated file name.
*
* Example: name of file with 2 contracts is {@code foo.groovy}, it will be
* converted by the implementation to {@code foo.json}. The recursive file
* converter will create two files {@code 0_foo.json} and {@code 1_foo.json}
*/</strong>
String generateOutputFileNameForInput(String inputFileName)
}</pre><p>Again, you must provide a <code class="literal">spring.factories</code> file, such as the one shown in the following
example:</p><pre class="screen"># Stub converters
org.springframework.cloud.contract.verifier.converter.StubGenerator=\
org.springframework.cloud.contract.verifier.wiremock.DslToWireMockClientConverter</pre><p>The default implementation is the WireMock stub generation.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You can provide multiple stub generator implementations. For example, from a single
DSL, you can produce both WireMock stubs and Pact files.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_the_custom_stub_runner" href="#_using_the_custom_stub_runner"></a>94.4&nbsp;Using the Custom Stub Runner</h2></div></div></div><p>If you decide to use a custom stub generation, you also need a custom way of running
stubs with your different stub provider.</p><p>Assume that you use <a class="link" href="https://github.com/dreamhead/moco" target="_top">Moco</a> to build your stubs and that
you have written a stub generator and placed your stubs in a JAR file.</p><p>In order for Stub Runner to know how to run your stubs, you have to define a custom
HTTP Stub server implementation, which might resemble the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> org.springframework.cloud.contract.stubrunner.provider.moco
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.github.dreamhead.moco.bootstrap.arg.HttpArgs
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.github.dreamhead.moco.runner.JsonRunner
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.github.dreamhead.moco.runner.RunnerSetting
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> groovy.util.logging.Commons
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.stubrunner.HttpServerStub
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.util.SocketUtils
<em><span class="hl-annotation" style="color: gray">@Commons</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MocoHttpServerStub <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> HttpServerStub {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> started
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> JsonRunner runner
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (!isRunning()) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> -<span class="hl-number">1</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> port
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isRunning() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> started
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
HttpServerStub start() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> start(SocketUtils.findAvailableTcpPort())
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
HttpServerStub start(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> port) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.port = port
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
HttpServerStub stop() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (!isRunning()) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.runner.stop()
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
HttpServerStub registerMappings(Collection&lt;File&gt; stubFiles) {
List&lt;RunnerSetting&gt; settings = stubFiles.findAll { it.name.endsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"json"</span>) }
.collect {
log.info(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Trying to parse [${it.name}]"</span>)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> RunnerSetting.aRunnerSetting().withStream(it.newInputStream()).build()
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (Exception e) {
log.warn(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Exception occurred while trying to parse file [${it.name}]"</span>, e)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null
}
}.findAll { it }
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.runner = JsonRunner.newJsonRunnerWithSetting(settings,
HttpArgs.httpArgs().withPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.port).build())
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.runner.run()
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.started = true
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
String registeredMappings() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span>
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> isAccepted(File file) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> file.name.endsWith(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">".json"</span>)
}
}</pre><p>Then, you can register it in your <code class="literal">spring.factories</code> file, as shown in the following
example:</p><pre class="screen">org.springframework.cloud.contract.stubrunner.HttpServerStub=\
org.springframework.cloud.contract.stubrunner.provider.moco.MocoHttpServerStub</pre><p>Now you can run stubs with Moco.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you do not provide any implementation, then the default (WireMock)
implementation is used. If you provide more than one, the first one on the list is used.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_the_custom_stub_downloader" href="#_using_the_custom_stub_downloader"></a>94.5&nbsp;Using the Custom Stub Downloader</h2></div></div></div><p>You can customize the way your stubs are downloaded by creating an implementation of the
<code class="literal">StubDownloaderBuilder</code> interface, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomStubDownloaderBuilder <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> StubDownloaderBuilder {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> StubDownloader build(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> StubRunnerOptions stubRunnerOptions) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubDownloader() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Map.Entry&lt;StubConfiguration, File&gt; downloadAndUnpackStubJar(
StubConfiguration config) {
File unpackedStubs = retrieveStubs();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AbstractMap.SimpleEntry&lt;&gt;(
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> StubConfiguration(config.getGroupId(), config.getArtifactId(), version,
config.getClassifier()), unpackedStubs);
}
File retrieveStubs() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// here goes your custom logic to provide a folder where all the stubs reside</span>
}
}</pre><p>Then you can register it in your <code class="literal">spring.factories</code> file, as shown in the following
example:</p><pre class="screen"># Example of a custom Stub Downloader Provider
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder=\
com.example.CustomStubDownloaderBuilder</pre><p>Now you can pick a folder with the source of your stubs.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you do not provide any implementation, then the default is used (scan classpath).
If you provide the <code class="literal">stubsMode = StubRunnerProperties.StubsMode.LOCAL</code> or
<code class="literal">, stubsMode = StubRunnerProperties.StubsMode.REMOTE</code> then the Aether implementation will be used
If you provide more than one, then the first one on the list is used.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="scm-stub-downloader" href="#scm-stub-downloader"></a>94.6&nbsp;Using the SCM Stub Downloader</h2></div></div></div><p>Whenever the <code class="literal">repositoryRoot</code> starts with a SCM protocol
(currently we support only <code class="literal">git://</code>), the stub downloader will try
to clone the repository and use it as a source of contracts
to generate tests or stubs.</p><p>Either via environment variables, system properties, properties set
inside the plugin or contracts repository configuration you can
tweak the downloader&#8217;s behaviour. Below you can find the list of
properties</p><div class="table"><a name="d0e27819" href="#d0e27819"></a><p class="title"><b>Table&nbsp;94.1.&nbsp;SCM Stub Downloader properties</b></p><div class="table-contents"><table summary="SCM Stub Downloader properties" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Type of a property</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Description</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">git.branch</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.git.branch</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_GIT_BRANCH</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>master</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Which branch to checkout</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">git.username</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.git.username</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_GIT_USERNAME</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Git clone username</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">git.password</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.git.password</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_GIT_PASSWORD</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Git clone password</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">git.no-of-attempts</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.git.no-of-attempts</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_GIT_NO_OF_ATTEMPTS</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Number of attempts to push the commits to <code class="literal">origin</code></p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">git.wait-between-attempts</code> (Plugin prop)
</p><p>* <code class="literal">stubrunner.properties.git.wait-between-attempts</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_GIT_WAIT_BETWEEN_ATTEMPTS</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>1000</p></td><td style="" align="left" valign="top"><p>Number of millis to wait between attempts to push the commits to <code class="literal">origin</code></p></td></tr></tbody></table></div></div><br class="table-break"></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="pact-stub-downloader" href="#pact-stub-downloader"></a>94.7&nbsp;Using the Pact Stub Downloader</h2></div></div></div><p>Whenever the <code class="literal">repositoryRoot</code> starts with a Pact protocol
(starts with <code class="literal">pact://</code>), the stub downloader will try
to fetch the Pact contract definitions from the Pact Broker.
Whatever is set after <code class="literal">pact://</code> will be parsed as the Pact Broker URL.</p><p>Either via environment variables, system properties, properties set
inside the plugin or contracts repository configuration you can
tweak the downloader&#8217;s behaviour. Below you can find the list of
properties</p><div class="table"><a name="d0e27968" href="#d0e27968"></a><p class="title"><b>Table&nbsp;94.2.&nbsp;SCM Stub Downloader properties</b></p><div class="table-contents"><table summary="SCM Stub Downloader properties" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of a property</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Description</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">pactbroker.host</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.pactbroker.host</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_PACTBROKER_HOST</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Host from URL passed to <code class="literal">repositoryRoot</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>What is the URL of Pact Broker</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">pactbroker.port</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.pactbroker.port</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_PACTBROKER_PORT</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Port from URL passed to <code class="literal">repositoryRoot</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>What is the port of Pact Broker</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">pactbroker.protocol</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.pactbroker.protocol</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_PACTBROKER_PROTOCOL</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Protocol from URL passed to <code class="literal">repositoryRoot</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>What is the protocol of Pact Broker</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">pactbroker.tags</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.pactbroker.tags</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_PACTBROKER_TAGS</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Version of the stub, or <code class="literal">latest</code> if version is <code class="literal">+</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>What tags should be used to fetch the stub</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">pactbroker.auth.scheme</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.pactbroker.auth.scheme</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_SCHEME</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Basic</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>What kind of authentication should be used to connect to the Pact Broker</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">pactbroker.auth.username</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.pactbroker.auth.username</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_USERNAME</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The username passed to <code class="literal">contractsRepositoryUsername</code> (maven) or <code class="literal">contractRepository.username</code> (gradle)</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Username used to connect to the Pact Broker</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">pactbroker.auth.password</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.pactbroker.auth.password</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_PACTBROKER_AUTH_PASSWORD</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The password passed to <code class="literal">contractsRepositoryPassword</code> (maven) or <code class="literal">contractRepository.password</code> (gradle)</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password used to connect to the Pact Broker</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>* <code class="literal">pactbroker.provider-name-with-group-id</code> (plugin prop)
</p><p>* <code class="literal">stubrunner.properties.pactbroker.provider-name-with-group-id</code> (system prop)
</p><p>* <code class="literal">STUBRUNNER_PROPERTIES_PACTBROKER_PROVIDER_NAME_WITH_GROUP_ID</code> (env prop)</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="" align="left" valign="top"><p>When <code class="literal">true</code>, the provider name will be a combination of <code class="literal">groupId:artifactId</code>. If <code class="literal">false</code>, just <code class="literal">artifactId</code> is used</p></td></tr></tbody></table></div></div><br class="table-break"></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_spring_cloud_contract_wiremock" href="#_spring_cloud_contract_wiremock"></a>95.&nbsp;Spring Cloud Contract WireMock</h2></div></div></div><p>The Spring Cloud Contract WireMock modules let you use <a class="link" href="http://wiremock.org" target="_top">WireMock</a> in a
Spring Boot application. Check out the
<a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/tree/master/samples" target="_top">samples</a>
for more details.</p><p>If you have a Spring Boot application that uses Tomcat as an embedded server (which is
the default with <code class="literal">spring-boot-starter-web</code>), you can add
<code class="literal">spring-cloud-starter-contract-stub-runner</code> to your classpath and add <code class="literal">@AutoConfigureWireMock</code> in
order to be able to use Wiremock in your tests. Wiremock runs as a stub server and you
can register stub behavior using a Java API or via static JSON declarations as part of
your test. The following code shows an example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureWireMock(port = 0)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WiremockForDocsTests {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// A service that calls out over HTTP</span>
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Service service;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Using the WireMock APIs in the normal way:</span>
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Stubbing WireMock</span>
stubFor(get(urlEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>)).willReturn(aResponse()
.withHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text/plain"</span>).withBody(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>)));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// We're asserting if WireMock responded properly</span>
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.service.go()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>);
}
}</pre><p>To start the stub server on a different port use (for example),
<code class="literal">@AutoConfigureWireMock(port=9999)</code>. For a random port, use a value of <code class="literal">0</code>. The stub
server port can be bound in the test application context with the "wiremock.server.port"
property. Using <code class="literal">@AutoConfigureWireMock</code> adds a bean of type <code class="literal">WiremockConfiguration</code> to
your test application context, where it will be cached in between methods and classes
having the same context, the same as for Spring integration tests. Also you can inject a bean of type <code class="literal">WireMockServer</code> into your test.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_stubs_automatically" href="#_registering_stubs_automatically"></a>95.1&nbsp;Registering Stubs Automatically</h2></div></div></div><p>If you use <code class="literal">@AutoConfigureWireMock</code>, it registers WireMock JSON stubs from the file
system or classpath (by default, from <code class="literal">file:src/test/resources/mappings</code>). You can
customize the locations using the <code class="literal">stubs</code> attribute in the annotation, which can be an
Ant-style resource pattern or a directory. In the case of a directory, <code class="literal"><span class="strong"><strong>*/</strong></span>.json</code> is
appended. The following code shows an example:</p><pre class="screen">@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureWireMock(stubs="classpath:/stubs")
public class WiremockImportApplicationTests {
@Autowired
private Service service;
@Test
public void contextLoads() throws Exception {
assertThat(this.service.go()).isEqualTo("Hello World!");
}
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Actually, WireMock always loads mappings from <code class="literal">src/test/resources/mappings</code> <span class="strong"><strong>as
well as</strong></span> the custom locations in the stubs attribute. To change this behavior, you can
also specify a files root as described in the next section of this document.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_using_files_to_specify_the_stub_bodies" href="#_using_files_to_specify_the_stub_bodies"></a>95.2&nbsp;Using Files to Specify the Stub Bodies</h2></div></div></div><p>WireMock can read response bodies from files on the classpath or the file system. In that
case, you can see in the JSON DSL that the response has a <code class="literal">bodyFileName</code> instead of a
(literal) <code class="literal">body</code>. The files are resolved relative to a root directory (by default,
<code class="literal">src/test/resources/__files</code>). To customize this location you can set the <code class="literal">files</code>
attribute in the <code class="literal">@AutoConfigureWireMock</code> annotation to the location of the parent
directory (in other words, <code class="literal">__files</code> is a subdirectory). You can use Spring resource
notation to refer to <code class="literal">file:&#8230;&#8203;</code> or <code class="literal">classpath:&#8230;&#8203;</code> locations. Generic URLs are not
supported. A list of values can be given, in which case WireMock resolves the first file
that exists when it needs to find a response body.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When you configure the <code class="literal">files</code> root, it also affects the
automatic loading of stubs, because they come from the root location
in a subdirectory called "mappings". The value of <code class="literal">files</code> has no
effect on the stubs loaded explicitly from the <code class="literal">stubs</code> attribute.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_alternative_using_junit_rules" href="#_alternative_using_junit_rules"></a>95.3&nbsp;Alternative: Using JUnit Rules</h2></div></div></div><p>For a more conventional WireMock experience, you can use JUnit <code class="literal">@Rules</code> to start and stop
the server. To do so, use the <code class="literal">WireMockSpring</code> convenience class to obtain an <code class="literal">Options</code>
instance, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WiremockForDocsClassRuleTests {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start WireMock on some dynamic port</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// for some reason `dynamicPort()` is not working properly</span>
<em><span class="hl-annotation" style="color: gray">@ClassRule</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> WireMockClassRule wiremock = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WireMockClassRule(
WireMockSpring.options().dynamicPort());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// A service that calls out over HTTP to localhost:${wiremock.port}</span>
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Service service;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Using the WireMock APIs in the normal way:</span>
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Stubbing WireMock</span>
wiremock.stubFor(get(urlEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>)).willReturn(aResponse()
.withHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text/plain"</span>).withBody(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>)));
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// We're asserting if WireMock responded properly</span>
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.service.go()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>);
}
}</pre><p>The <code class="literal">@ClassRule</code> means that the server shuts down after all the methods in this class
have been run.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_relaxed_ssl_validation_for_rest_template" href="#_relaxed_ssl_validation_for_rest_template"></a>95.4&nbsp;Relaxed SSL Validation for Rest Template</h2></div></div></div><p>WireMock lets you stub a "secure" server with an "https" URL protocol. If your
application wants to contact that stub server in an integration test, it will find that
the SSL certificates are not valid (the usual problem with self-installed certificates).
The best option is often to re-configure the client to use "http". If that&#8217;s not an
option, you can ask Spring to configure an HTTP client that ignores SSL validation errors
(do so only for tests, of course).</p><p>To make this work with minimum fuss, you need to be using the Spring Boot
<code class="literal">RestTemplateBuilder</code> in your app, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> RestTemplate restTemplate(RestTemplateBuilder builder) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> builder.build();
}</pre><p>You need <code class="literal">RestTemplateBuilder</code> because the builder is passed through callbacks to
initialize it, so the SSL validation can be set up in the client at that point. This
happens automatically in your test if you are using the <code class="literal">@AutoConfigureWireMock</code>
annotation or the stub runner. If you use the JUnit <code class="literal">@Rule</code> approach, you need to add the
<code class="literal">@AutoConfigureHttpClient</code> annotation as well, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest("app.baseUrl=https://localhost:6443")</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureHttpClient</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WiremockHttpsServerApplicationTests {
<em><span class="hl-annotation" style="color: gray">@ClassRule</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> WireMockClassRule wiremock = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WireMockClassRule(
WireMockSpring.options().httpsPort(<span class="hl-number">6443</span>));
...
}</pre><p>If you are using <code class="literal">spring-boot-starter-test</code>, you have the Apache HTTP client on the
classpath and it is selected by the <code class="literal">RestTemplateBuilder</code> and configured to ignore SSL
errors. If you use the default <code class="literal">java.net</code> client, you do not need the annotation (but it
won&#8217;t do any harm). There is no support currently for other clients, but it may be added
in future releases.</p><p>To disable the custom <code class="literal">RestTemplateBuilder</code>, set the <code class="literal">wiremock.rest-template-ssl-enabled</code>
property to <code class="literal">false</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_wiremock_and_spring_mvc_mocks" href="#_wiremock_and_spring_mvc_mocks"></a>95.5&nbsp;WireMock and Spring MVC Mocks</h2></div></div></div><p>Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into
a Spring <code class="literal">MockRestServiceServer</code>. The following code shows an example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest(webEnvironment = WebEnvironment.NONE)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> WiremockForDocsMockServerApplicationTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> RestTemplate restTemplate;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> Service service;
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// will read stubs classpath</span>
MockRestServiceServer server = WireMockRestServiceServer.with(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.restTemplate)
.baseUrl(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://example.org"</span>).stubs(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"classpath:/stubs/resource.json"</span>)
.build();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// We're asserting if WireMock responded properly</span>
assertThat(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.service.go()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>);
server.verify();
}
}</pre><p>The <code class="literal">baseUrl</code> value is prepended to all mock calls, and the <code class="literal">stubs()</code> method takes a stub
path resource pattern as an argument. In the preceding example, the stub defined at
<code class="literal">/stubs/resource.json</code> is loaded into the mock server. If the <code class="literal">RestTemplate</code> is asked to
visit <code class="literal"><a class="link" href="http://example.org/" target="_top">http://example.org/</a></code>, it gets the responses as being declared at that URL. More
than one stub pattern can be specified, and each one can be a directory (for a recursive
list of all ".json"), a fixed filename (as in the example above), or an Ant-style
pattern. The JSON format is the normal WireMock format, which you can read about in the
<a class="link" href="http://wiremock.org/docs/stubbing/" target="_top">WireMock website</a>.</p><p>Currently, the Spring Cloud Contract Verifier supports Tomcat, Jetty, and Undertow as
Spring Boot embedded servers, and Wiremock itself has "native" support for a particular
version of Jetty (currently 9.2). To use the native Jetty, you need to add the native
Wiremock dependencies and exclude the Spring Boot container (if there is one).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customization_of_wiremock_configuration" href="#_customization_of_wiremock_configuration"></a>95.6&nbsp;Customization of WireMock configuration</h2></div></div></div><p>You can register a bean of <code class="literal">org.springframework.cloud.contract.wiremock.WireMockConfigurationCustomizer</code> type
in order to customize the WireMock configuration (e.g. add custom transformers).
Example:</p><pre class="programlisting"> <em><span class="hl-annotation" style="color: gray">@Bean</span></em>
WireMockConfigurationCustomizer optionsCustomizer() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> WireMockConfigurationCustomizer() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> customize(WireMockConfiguration options) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// perform your customization here</span>
}
};
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_generating_stubs_using_rest_docs" href="#_generating_stubs_using_rest_docs"></a>95.7&nbsp;Generating Stubs using REST Docs</h2></div></div></div><p><a class="link" href="https://projects.spring.io/spring-restdocs" target="_top">Spring REST Docs</a> can be used to generate
documentation (for example in Asciidoctor format) for an HTTP API with Spring MockMvc
or <code class="literal">WebTestClient</code> or Rest Assured. At the same time that you generate documentation for your API, you can also
generate WireMock stubs by using Spring Cloud Contract WireMock. To do so, write your
normal REST Docs test cases and use <code class="literal">@AutoConfigureRestDocs</code> to have stubs be
automatically generated in the REST Docs output directory. The following code shows an
example using <code class="literal">MockMvc</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureRestDocs(outputDir = "target/snippets")</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureMockMvc</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ApplicationTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MockMvc mockMvc;
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
mockMvc.perform(get(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>))
.andExpect(content().string(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>))
.andDo(document(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"resource"</span>));
}
}</pre><p>This test generates a WireMock stub at "target/snippets/stubs/resource.json". It matches
all GET requests to the "/resource" path. The same example with <code class="literal">WebTestClient</code> (used
for testing Spring WebFlux applications) would look like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureRestDocs(outputDir = "target/snippets")</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureWebTestClient</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ApplicationTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> WebTestClient client;
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
client.get().uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>).exchange()
.expectBody(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span>)
.consumeWith(document(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"resource"</span>));
}
}</pre><p>Without any additional configuration, these tests create a stub with a request matcher
for the HTTP method and all headers except "host" and "content-length". To match the
request more precisely (for example, to match the body of a POST or PUT), we need to
explicitly create a request matcher. Doing so has two effects:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Creating a stub that matches only in the way you specify.</li><li class="listitem">Asserting that the request in the test case also matches the same conditions.</li></ul></div><p>The main entry point for this feature is <code class="literal">WireMockRestDocs.verify()</code>, which can be used
as a substitute for the <code class="literal">document()</code> convenience method, as shown in the following
example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocs.verify;</pre><pre class="screen">@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureRestDocs(outputDir = "target/snippets")
@AutoConfigureMockMvc
public class ApplicationTests {
@Autowired
private MockMvc mockMvc;
@Test
public void contextLoads() throws Exception {
mockMvc.perform(post("/resource")
.content("{\"id\":\"123456\",\"message\":\"Hello World\"}"))
.andExpect(status().isOk())
.andDo(verify().jsonPath("$.id")
.stub("resource"));
}
}</pre><p>This contract specifies that any valid POST with an "id" field receives the response
defined in this test. You can chain together calls to <code class="literal">.jsonPath()</code> to add additional
matchers. If JSON Path is unfamiliar, The <a class="link" href="https://github.com/jayway/JsonPath" target="_top">JayWay
documentation</a> can help you get up to speed. The <code class="literal">WebTestClient</code> version of this test
has a similar <code class="literal">verify()</code> static helper that you insert in the same place.</p><p>Instead of the <code class="literal">jsonPath</code> and <code class="literal">contentType</code> convenience methods, you can also use the
WireMock APIs to verify that the request matches the created stub, as shown in the
following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> contextLoads() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
mockMvc.perform(post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>)
.content(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"id\":\"123456\",\"message\":\"Hello World\"}"</span>))
.andExpect(status().isOk())
.andDo(verify()
.wiremock(WireMock.post(
urlPathEquals(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span>))
.withRequestBody(matchingJsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.id"</span>))
.stub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"post-resource"</span>));
}</pre><p>The WireMock API is rich. You can match headers, query parameters, and request body by
regex as well as by JSON path. These features can be used to create stubs with a wider
range of parameters. The above example generates a stub resembling the following example:</p><p><b>post-resource.json.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"request"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"url"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/resource"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"method"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"POST"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bodyPatterns"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"matchesJsonPath"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$.id"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"response"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span> : <span class="hl-number">200</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"body"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"headers"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"X-Application-Context"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application:-1"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Content-Type"</span> : <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"text/plain"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>You can use either the <code class="literal">wiremock()</code> method or the <code class="literal">jsonPath()</code> and <code class="literal">contentType()</code>
methods to create request matchers, but you can&#8217;t use both approaches.</p></td></tr></table></div><p>On the consumer side, you can make the <code class="literal">resource.json</code> generated earlier in this section
available on the classpath (by
&lt;&lt;publishing-stubs-as-jars], for example). After that, you can create a stub using WireMock in a
number of different ways, including by using
<code class="literal">@AutoConfigureWireMock(stubs="classpath:resource.json")</code>, as described earlier in this
document.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_generating_contracts_by_using_rest_docs" href="#_generating_contracts_by_using_rest_docs"></a>95.8&nbsp;Generating Contracts by Using REST Docs</h2></div></div></div><p>You can also generate Spring Cloud Contract DSL files and documentation with Spring REST
Docs. If you do so in combination with Spring Cloud WireMock, you get both the contracts
and the stubs.</p><p>Why would you want to use this feature? Some people in the community asked questions
about a situation in which they would like to move to DSL-based contract definition,
but they already have a lot of Spring MVC tests. Using this feature lets you generate
the contract files that you can later modify and move to folders (defined in your
configuration) so that the plugin finds them.</p><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>You might wonder why this functionality is in the WireMock module. The functionality
is there because it makes sense to generate both the contracts and the stubs.</p></td></tr></table></div><p>Consider the following test:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.mockMvc
.perform(post(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foo"</span>).accept(MediaType.APPLICATION_PDF)
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.content(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"{\"foo\": 23, \"bar\" : \"baz\" }"</span>))
.andExpect(status().isOk()).andExpect(content().string(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar"</span>))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// first WireMock</span>
.andDo(WireMockRestDocs.verify().jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.foo &gt;= 20)]"</span>)
.jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"$[?(@.bar in ['baz','bazz','bazzz'])]"</span>)
.contentType(MediaType.valueOf(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"application/json"</span>))
.stub(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"shouldGrantABeerIfOldEnough"</span>))
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// then Contract DSL documentation</span>
.andDo(document(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"index"</span>, SpringCloudContractRestDocs.dslContract()));</pre><p>The preceding test creates the stub presented in the previous section, generating both
the contract and a documentation file.</p><p>The contract is called <code class="literal">index.groovy</code> and might look like the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
Contract.make {
request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/foo'</span>
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> {<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>: <span class="hl-number">23</span> }
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">')
</span> headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Accept'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
}
}
response {
status OK()
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'
</span> bar
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">')
</span> headers {
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Type'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'application/json;charset=UTF-8'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Content-Length'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'3'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">''</span>)
}
testMatchers {
jsonPath(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'$[?(@.foo &gt;= 20)]'</span>, byType())
}
}
}</pre><p>The generated document (formatted in Asciidoc in this case) contains a formatted
contract. The location of this file would be <code class="literal">index/dsl-contract.adoc</code>.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_migrations" href="#_migrations"></a>96.&nbsp;Migrations</h2></div></div></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>For up to date migration guides please visit
the project&#8217;s <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/wiki/" target="_top">wiki page</a>.</p></td></tr></table></div><p>This section covers migrating from one version of Spring Cloud Contract Verifier to the
next version. It covers the following versions upgrade paths:</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cloud-verifier-1.0-1.1" href="#cloud-verifier-1.0-1.1"></a>96.1&nbsp;1.0.x &#8594; 1.1.x</h2></div></div></div><p>This section covers upgrading from version 1.0 to version 1.1.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_new_structure_of_generated_stubs" href="#_new_structure_of_generated_stubs"></a>96.1.1&nbsp;New structure of generated stubs</h3></div></div></div><p>In <code class="literal">1.1.x</code> we have introduced a change to the structure of generated stubs. If you have
been using the <code class="literal">@AutoConfigureWireMock</code> notation to use the stubs from the classpath,
it no longer works. The following example shows how the <code class="literal">@AutoConfigureWireMock</code> notation
used to work:</p><pre class="programlisting">@AutoConfigureWireMock(stubs = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"classpath:/customer-stubs/mappings"</span>, port = <span class="hl-number">8084</span>)</pre><p>You must either change the location of the stubs to:
<code class="literal">classpath:&#8230;&#8203;/META-INF/groupId/artifactId/version/mappings</code> or use the new
classpath-based <code class="literal">@AutoConfigureStubRunner</code>, as shown in the following example:</p><pre class="programlisting">@AutoConfigureWireMock(stubs = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"classpath:customer-stubs/META-INF/travel.components/customer-contract/1.0.2-SNAPSHOT/mappings/"</span>, port = <span class="hl-number">8084</span>)</pre><p>If you do not want to use <code class="literal">@AutoConfigureStubRunner</code> and you want to remain with the old
structure, set your plugin tasks accordingly. The following example would work for the
structure presented in the previous snippet.</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- start of pom.xml --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- we don't want the verifier to do a jar for us --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;spring.cloud.contract.verifier.skip&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/spring.cloud.contract.verifier.skip&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/properties&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- ... --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- You need to set up the assembly plugin --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.apache.maven.plugins<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>maven-assembly-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>stub<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;phase&gt;</span>prepare-package<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/phase&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;goal&gt;</span>single<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goal&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/goals&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;inherited&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/inherited&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;attach&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/attach&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;descriptor&gt;</span>$../../../../src/assembly/stub.xml<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/descriptor&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/execution&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/executions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- end of pom.xml --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- start of stub.xml--&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;assembly</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xmlns:xsi</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://www.w3.org/2001/XMLSchema-instance"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">xsi:schemaLocation</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;id&gt;</span>stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/id&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;format&gt;</span>jar<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/format&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/formats&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includeBaseDirectory&gt;</span>false<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includeBaseDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>${project.build.directory}/snippets/stubs<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>customer-stubs/mappings<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;directory&gt;</span>$../../../../src/test/resources/contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/directory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;outputDirectory&gt;</span>customer-stubs/contracts<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/outputDirectory&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;include&gt;</span>**/*.groovy<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/include&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/includes&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSet&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/fileSets&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/assembly&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- end of stub.xml--&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">task copyStubs(type: Copy, dependsOn: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'generateWireMockClientStubs'</span>) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Preserve directory structure from 1.0.X of spring-cloud-contract</span>
from <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/resources/main/customer-stubs/META-INF/${project.group}/${project.name}/${project.version}"</span>
into <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${project.buildDir}/resources/main/customer-stubs"</span>
}</pre><p class="secondary">
</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cloud-verifier-1.1-1.2" href="#cloud-verifier-1.1-1.2"></a>96.2&nbsp;1.1.x &#8594; 1.2.x</h2></div></div></div><p>This section covers upgrading from version 1.1 to version 1.2.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_literal_httpserverstub_literal" href="#_custom_literal_httpserverstub_literal"></a>96.2.1&nbsp;Custom <code class="literal">HttpServerStub</code></h3></div></div></div><p><code class="literal">HttpServerStub</code> includes a method that was not in version 1.1. The method is
<code class="literal">String registeredMappings()</code> If you have classes that implement <code class="literal">HttpServerStub</code>, you
now have to implement the <code class="literal">registeredMappings()</code> method. It should return a <code class="literal">String</code>
representing all mappings available in a single <code class="literal">HttpServerStub</code>.</p><p>See <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/issues/355" target="_top">issue 355</a> for more
detail.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_new_packages_for_generated_tests" href="#_new_packages_for_generated_tests"></a>96.2.2&nbsp;New packages for generated tests</h3></div></div></div><p>The flow for setting the generated tests package name will look like this:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Set <code class="literal">basePackageForTests</code></li><li class="listitem">If <code class="literal">basePackageForTests</code> was not set, pick the package from <code class="literal">baseClassForTests</code></li><li class="listitem">If <code class="literal">baseClassForTests</code> was not set, pick <code class="literal">packageWithBaseClasses</code></li><li class="listitem">If nothing got set, pick the default value:
<code class="literal">org.springframework.cloud.contract.verifier.tests</code></li></ul></div><p>See <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/issues/260" target="_top">issue 260</a> for more
detail.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_new_methods_in_templateprocessor" href="#_new_methods_in_templateprocessor"></a>96.2.3&nbsp;New Methods in TemplateProcessor</h3></div></div></div><p>In order to add support for <code class="literal">fromRequest.path</code>, the following methods had to be added to the
<code class="literal">TemplateProcessor</code> interface:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">path()</code></li><li class="listitem"><code class="literal">path(int index)</code></li></ul></div><p>See <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/issues/388" target="_top">issue 388</a> for more
detail.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_restassured_3_0" href="#_restassured_3_0"></a>96.2.4&nbsp;RestAssured 3.0</h3></div></div></div><p>Rest Assured, used in the generated test classes, got bumped to <code class="literal">3.0</code>. If
you manually set versions of Spring Cloud Contract and the release train
you might see the following exception:</p><pre class="programlisting">Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:<span class="hl-number">3.1</span>:testCompile (default-testCompile) on project some-project: Compilation failure: Compilation failure:
[ERROR] /some/path/SomeClass.java:[<span class="hl-number">4</span>,<span class="hl-number">39</span>] package com.jayway.restassured.response does not exist</pre><p>This exception will occur due to the fact that the tests got generated with
an old version of plugin and at test execution time you have an incompatible
version of the release train (and vice versa).</p><p>Done via <a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/issues/267" target="_top">issue 267</a></p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="cloud-verifier-1.2-2.0" href="#cloud-verifier-1.2-2.0"></a>96.3&nbsp;1.2.x &#8594; 2.0.x</h2></div></div></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_links" href="#_links"></a>97.&nbsp;Links</h2></div></div></div><p>The following links may be helpful when working with Spring Cloud Contract:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://github.com/spring-cloud/spring-cloud-contract/" target="_top">Spring Cloud Contract Github
Repository</a></li><li class="listitem"><a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples/" target="_top">Spring Cloud
Contract Samples</a></li><li class="listitem"><a class="link" href="https://gitter.im/spring-cloud/spring-cloud-contract" target="_top">Spring Cloud Contract Gitter</a></li><li class="listitem"><a class="link" href="https://www.youtube.com/watch?v=sAAklvxmPmk" target="_top">Spring Cloud Contract WJUG Presentation by
Marcin Grzejszczak</a></li></ul></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_vault" href="#_spring_cloud_vault"></a>Part&nbsp;XIV.&nbsp;Spring Cloud Vault</h1></div></div></div><div class="partintro"><div></div><p>&copy; 2016-2018 The original authors.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><span class="emphasis"><em>Copies of this document may be made for your own use and for distribution to others, provided that you do not charge any fee for such copies and further provided that each copy contains this Copyright Notice, whether distributed in print or electronically.</em></span></p></td></tr></table></div><p>Spring Cloud Vault Config provides client-side support for externalized configuration in a distributed system. With <a class="link" href="https://www.vaultproject.io" target="_top">HashiCorp&#8217;s Vault</a> you have a central place to manage external secret properties for applications across all environments. Vault can manage static and dynamic secrets such as username/password for remote applications/resources and provide credentials for external services such as MySQL, PostgreSQL, Apache Cassandra, MongoDB, Consul, AWS and more.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_quick_start_4" href="#_quick_start_4"></a>98.&nbsp;Quick Start</h2></div></div></div><p><span class="strong"><strong>Prerequisites</strong></span></p><p>To get started with Vault and this guide you need a
*NIX-like operating systems that provides:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">wget</code>, <code class="literal">openssl</code> and <code class="literal">unzip</code></li><li class="listitem">at least Java 7 and a properly configured <code class="literal">JAVA_HOME</code> environment variable</li></ul></div><p><span class="strong"><strong>Install Vault</strong></span></p><pre class="programlisting">$ src/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>/bash/install_vault.sh</pre><p><span class="strong"><strong>Create SSL certificates for Vault</strong></span></p><pre class="programlisting">$ src/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>/bash/create_certificates.sh</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p><code class="literal">create_certificates.sh</code> creates certificates in <code class="literal">work/ca</code> and a JKS truststore <code class="literal">work/keystore.jks</code>. If you want to run Spring Cloud Vault using this quickstart guide you need to configure the truststore the <code class="literal">spring.cloud.vault.ssl.trust-store</code> property to <code class="literal">file:work/keystore.jks</code>.</p></td></tr></table></div><p><a name="quickstart.vault.start" href="#quickstart.vault.start"></a><span class="strong"><strong>Start Vault server</strong></span></p><pre class="programlisting">$ src/<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">test</span>/bash/local_run_vault.sh</pre><p>Vault is started listening on <code class="literal">0.0.0.0:8200</code> using the <code class="literal">inmem</code> storage and
<code class="literal">https</code>.
Vault is sealed and not initialized when starting up.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you want to run tests, leave Vault uninitialized. The tests will
initialize Vault and create a root token <code class="literal">00000000-0000-0000-0000-000000000000</code>.</p></td></tr></table></div><p>If you want to use Vault for your application or give it a try then you need to initialize it first.</p><pre class="programlisting">$ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">export</span> VAULT_ADDR=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://localhost:8200"</span>
$ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">export</span> VAULT_SKIP_VERIFY=true <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Don't do this for production</span>
$ vault init</pre><p>You should see something like:</p><pre class="programlisting">Key <span class="hl-number">1</span>: <span class="hl-number">7149</span>c6a2e16b8833f6eb1e76df03e47f6113a3288b3093faf5033d44f0e70fe701
Key <span class="hl-number">2</span>: <span class="hl-number">901</span>c534c7988c18c20435a85213c683bdcf0efcd82e38e2893779f152978c18c02
Key <span class="hl-number">3</span>: <span class="hl-number">03</span>ff3948575b1165a20c20ee7c3e6edf04f4cdbe0e82dbff5be49c63f98bc03a03
Key <span class="hl-number">4</span>: <span class="hl-number">216</span>ae5cc3ddaf93ceb8e1d15bb9fc3176653f5b738f5f3d1ee00cd7dccbe926e04
Key <span class="hl-number">5</span>: b2898fc8130929d569c1677ee69dc5f3be57d7c4b494a6062693ce0b1c4d93d805
Initial Root Token: <span class="hl-number">19</span>aefa97-cccc-bbbb-aaaa-<span class="hl-number">225940</span>e63d76
Vault initialized with <span class="hl-number">5</span> keys and a key threshold of <span class="hl-number">3.</span> Please
securely distribute the above keys. When the Vault is re-sealed,
restarted, or stopped, you must provide at least <span class="hl-number">3</span> of these keys
to unseal it again.
Vault does not store the master key. Without at least <span class="hl-number">3</span> keys,
your Vault will remain permanently sealed.</pre><p>Vault will initialize and return a set of unsealing keys and the root token.
Pick 3 keys and unseal Vault. Store the Vault token in the <code class="literal">VAULT_TOKEN</code>
environment variable.</p><pre class="programlisting">$ vault unseal (Key <span class="hl-number">1</span>)
$ vault unseal (Key <span class="hl-number">2</span>)
$ vault unseal (Key <span class="hl-number">3</span>)
$ <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">export</span> VAULT_TOKEN=(Root token)
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Required to run Spring Cloud Vault tests after manual initialization</span>
$ vault token-create -id=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"00000000-0000-0000-0000-000000000000"</span> -policy=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"root"</span></pre><p>Spring Cloud Vault accesses different resources. By default, the secret
backend is enabled which accesses secret config settings via JSON endpoints.</p><p>The HTTP service has resources in the form:</p><pre class="screen">/secret/{application}/{profile}
/secret/{application}
/secret/{defaultContext}/{profile}
/secret/{defaultContext}</pre><p>where the "application" is injected as the <code class="literal">spring.application.name</code> in the
<code class="literal">SpringApplication</code> (i.e. what is normally "application" in a regular
Spring Boot app), "profile" is an active profile (or comma-separated
list of properties). Properties retrieved from Vault will be used "as-is"
without further prefixing of the property names.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_client_side_usage_2" href="#_client_side_usage_2"></a>99.&nbsp;Client Side Usage</h2></div></div></div><p>To use these features in an application, just build it as a Spring
Boot application that depends on <code class="literal">spring-cloud-vault-config</code> (e.g. see
the test cases). Example Maven configuration:</p><div class="example"><a name="d0e28886" href="#d0e28886"></a><p class="title"><b>Example&nbsp;99.1.&nbsp;pom.xml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-parent<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>2.0.0.RELEASE<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;relativePath /&gt;</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- lookup parent from repository --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-vault-config<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>Greenwich.M3<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- repositories also needed for snapshots and milestones --&gt;</span></pre></div></div><br class="example-break"><p>Then you can create a standard Spring Boot application, like this simple HTTP server:</p><div class="informalexample"><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@RequestMapping("/")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre></div><p>When it runs it will pick up the external configuration from the
default local Vault server on port <code class="literal">8200</code> if it is running. To modify
the startup behavior you can change the location of the Vault server
using <code class="literal">bootstrap.properties</code> (like <code class="literal">application.properties</code> but for
the bootstrap phase of an application context), e.g.</p><div class="example"><a name="d0e28907" href="#d0e28907"></a><p class="title"><b>Example&nbsp;99.2.&nbsp;bootstrap.yml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> host</span>: localhost
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">8200</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> scheme</span>: https
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://localhost:<span class="hl-number">8200</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> connection-timeout</span>: <span class="hl-number">5000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> read-timeout</span>: <span class="hl-number">15000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> order</span>: -<span class="hl-number">10</span></pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">host</code> sets the hostname of the Vault host. The host name will be used
for SSL certificate validation</li><li class="listitem"><code class="literal">port</code> sets the Vault port</li><li class="listitem"><code class="literal">scheme</code> setting the scheme to <code class="literal">http</code> will use plain HTTP.
Supported schemes are <code class="literal">http</code> and <code class="literal">https</code>.</li><li class="listitem"><code class="literal">uri</code> configure the Vault endpoint with an URI. Takes precedence over host/port/scheme configuration</li><li class="listitem"><code class="literal">connection-timeout</code> sets the connection timeout in milliseconds</li><li class="listitem"><code class="literal">read-timeout</code> sets the read timeout in milliseconds</li><li class="listitem"><code class="literal">config.order</code> sets the order for the property source</li></ul></div><p>Enabling further integrations requires additional dependencies and
configuration. Depending on how you have set up Vault you might need
additional configuration like
<a class="link" href="http://cloud.spring.io/spring-cloud-vault/spring-cloud-vault.html#vault.config.ssl" target="_top">SSL</a> and
<a class="link" href="http://cloud.spring.io/spring-cloud-vault/spring-cloud-vault.html#vault.config.authentication" target="_top">authentication</a>.</p><p>If the application imports the <code class="literal">spring-boot-starter-actuator</code> project, the
status of the vault server will be available via the <code class="literal">/health</code> endpoint.</p><p>The vault health indicator can be enabled or disabled through the property <code class="literal">management.health.vault.enabled</code> (default to <code class="literal">true</code>).</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_authentication_2" href="#_authentication_2"></a>99.1&nbsp;Authentication</h2></div></div></div><p>Vault requires an <a class="link" href="https://www.vaultproject.io/docs/concepts/auth.html" target="_top">authentication mechanism</a> to <a class="link" href="https://www.vaultproject.io/docs/concepts/tokens.html" target="_top">authorize client requests</a>.</p><p>Spring Cloud Vault supports multiple <a class="link" href="http://cloud.spring.io/spring-cloud-vault/spring-cloud-vault.html#vault.config.authentication" target="_top">authentication mechanisms</a> to authenticate applications with Vault.</p><p>For a quickstart, use the root token printed by the <a class="link" href="#quickstart.vault.start">Vault initialization</a>.</p><div class="example"><a name="d0e29002" href="#d0e29002"></a><p class="title"><b>Example&nbsp;99.3.&nbsp;bootstrap.yml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> token</span>: <span class="hl-number">19</span>aefa97-cccc-bbbb-aaaa-<span class="hl-number">225940e63d</span>76</pre></div></div><br class="example-break"><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Consider carefully your security requirements. Static token authentication is fine if you want quickly get started with Vault, but a static token is not protected any further. Any disclosure to unintended parties allows Vault use with the associated token roles.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault.config.authentication" href="#vault.config.authentication"></a>100.&nbsp;Authentication methods</h2></div></div></div><p>Different organizations have different requirements for security
and authentication. Vault reflects that need by shipping multiple authentication
methods. Spring Cloud Vault supports token and AppId authentication.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.authentication.token" href="#vault.config.authentication.token"></a>100.1&nbsp;Token authentication</h2></div></div></div><p>Tokens are the core method for authentication within Vault.
Token authentication requires a static token to be provided using the
<a class="link" href="https://github.com/spring-cloud/spring-cloud-commons/blob/master/docs/src/main/asciidoc/spring-cloud-commons.adoc#the-bootstrap-application-context" target="_top">Bootstrap Application Context</a>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Token authentication is the default authentication method.
If a token is disclosed an unintended party gains access to Vault and
can access secrets for the intended client.</p></td></tr></table></div><div class="example"><a name="d0e29026" href="#d0e29026"></a><p class="title"><b>Example&nbsp;100.1.&nbsp;bootstrap.yml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: TOKEN
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> token</span>: <span class="hl-number">00000000</span>-<span class="hl-number">0000</span>-<span class="hl-number">0000</span>-<span class="hl-number">0000</span>-<span class="hl-number">000000000000</span></pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">authentication</code> setting this value to <code class="literal">TOKEN</code> selects the Token
authentication method</li><li class="listitem"><code class="literal">token</code> sets the static token to use</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/concepts/tokens.html" target="_top">Vault Documentation: Tokens</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.authentication.appid" href="#vault.config.authentication.appid"></a>100.2&nbsp;AppId authentication</h2></div></div></div><p>Vault supports <a class="link" href="https://www.vaultproject.io/docs/auth/app-id.html" target="_top">AppId</a>
authentication that consists of two hard to guess tokens. The AppId
defaults to <code class="literal">spring.application.name</code> that is statically configured.
The second token is the UserId which is a part determined by the application,
usually related to the runtime environment. IP address, Mac address or a
Docker container name are good examples. Spring Cloud Vault Config supports
IP address, Mac address and static UserId&#8217;s (e.g. supplied via System properties).
The IP and Mac address are represented as Hex-encoded SHA256 hash.</p><p>IP address-based UserId&#8217;s use the local host&#8217;s IP address.</p><div class="example"><a name="d0e29062" href="#d0e29062"></a><p class="title"><b>Example&nbsp;100.2.&nbsp;bootstrap.yml using SHA256 IP-Address UserId&#8217;s</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: APPID
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> app-id</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> user-id</span>: IP_ADDRESS</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">authentication</code> setting this value to <code class="literal">APPID</code> selects the AppId
authentication method</li><li class="listitem"><code class="literal">app-id-path</code> sets the path of the AppId mount to use</li><li class="listitem"><code class="literal">user-id</code> sets the UserId method. Possible values are <code class="literal">IP_ADDRESS</code>,
<code class="literal">MAC_ADDRESS</code> or a class name implementing a custom <code class="literal">AppIdUserIdMechanism</code></li></ul></div><p>The corresponding command to generate the IP address UserId from a command line is:</p><pre class="screen">$ echo -n 192.168.99.1 | sha256sum</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Including the line break of <code class="literal">echo</code> leads to a different hash value
so make sure to include the <code class="literal">-n</code> flag.</p></td></tr></table></div><p>Mac address-based UserId&#8217;s obtain their network device from the
localhost-bound device. The configuration also allows specifying
a <code class="literal">network-interface</code> hint to pick the right device. The value of
<code class="literal">network-interface</code> is optional and can be either an interface
name or interface index (0-based).</p><div class="example"><a name="d0e29115" href="#d0e29115"></a><p class="title"><b>Example&nbsp;100.3.&nbsp;bootstrap.yml using SHA256 Mac-Address UserId&#8217;s</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: APPID
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> app-id</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> user-id</span>: MAC_ADDRESS
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> network-interface</span>: eth0</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">network-interface</code> sets network interface to obtain the physical address</li></ul></div><p>The corresponding command to generate the IP address UserId from a command line is:</p><pre class="screen">$ echo -n 0AFEDE1234AC | sha256sum</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The Mac address is specified uppercase and without colons.
Including the line break of <code class="literal">echo</code> leads to a different hash value
so make sure to include the <code class="literal">-n</code> flag.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_custom_userid" href="#_custom_userid"></a>100.2.1&nbsp;Custom UserId</h3></div></div></div><p>The UserId generation is an open mechanism. You can set
<code class="literal">spring.cloud.vault.app-id.user-id</code> to any string and the configured
value will be used as static UserId.</p><p>A more advanced approach lets you set <code class="literal">spring.cloud.vault.app-id.user-id</code> to a
classname. This class must be on your classpath and must implement
the <code class="literal">org.springframework.cloud.vault.AppIdUserIdMechanism</code> interface
and the <code class="literal">createUserId</code> method. Spring Cloud Vault will obtain the UserId
by calling <code class="literal">createUserId</code> each time it authenticates using AppId to
obtain a token.</p><div class="example"><a name="d0e29161" href="#d0e29161"></a><p class="title"><b>Example&nbsp;100.4.&nbsp;bootstrap.yml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: APPID
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> app-id</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> user-id</span>: com.examlple.MyUserIdMechanism</pre></div></div><br class="example-break"><div class="example"><a name="d0e29166" href="#d0e29166"></a><p class="title"><b>Example&nbsp;100.5.&nbsp;MyUserIdMechanism.java</b></p><div class="example-contents"><pre class="programlisting">public class MyUserIdMechanism implements AppIdUserIdMechanism <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
public String createUserId() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> String userId </span>= ...
return userId;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre></div></div><br class="example-break"><p>See also: <a class="link" href="https://www.vaultproject.io/docs/auth/app-id.html" target="_top">Vault Documentation: Using the App ID auth backend</a></p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_approle_authentication" href="#_approle_authentication"></a>100.3&nbsp;AppRole authentication</h2></div></div></div><p><a class="link" href="https://www.vaultproject.io/docs/auth/app-id.html" target="_top">AppRole</a> is intended for machine
authentication, like the deprecated (since Vault 0.6.1) <a class="xref" href="#vault.config.authentication.appid" title="100.2&nbsp;AppId authentication">Section&nbsp;100.2, &#8220;AppId authentication&#8221;</a>.
AppRole authentication consists of two hard to guess (secret) tokens: RoleId and SecretId.</p><p>Spring Vault supports various AppRole scenarios (push/pull mode and wrapped).</p><p>RoleId and optionally SecretId must be provided by configuration,
Spring Vault will not look up these or create a custom SecretId.</p><div class="example"><a name="d0e29188" href="#d0e29188"></a><p class="title"><b>Example&nbsp;100.6.&nbsp;bootstrap.yml with AppRole authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: APPROLE
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> app-role</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role-id</span>: bde2076b-cccb-<span class="hl-number">3</span>cf0-d57e-bca7b1e83a52</pre></div></div><br class="example-break"><p>The following scenarios are supported along the required configuration details:</p><div class="table"><a name="d0e29195" href="#d0e29195"></a><p class="title"><b>Table&nbsp;100.1.&nbsp;Configuration</b></p><div class="table-contents"><table summary="Configuration" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"><col class="col_4"><col class="col_5"></colgroup><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>Method</strong></span></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>RoleId</strong></span></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>SecretId</strong></span></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>RoleName</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>Token</strong></span></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided RoleId/SecretId</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided RoleId without SecretId</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided RoleId, Pull SecretId</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pull RoleId, provided SecretId</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Full Pull Mode</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wrapped</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wrapped RoleId, provided SecretId</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Provided RoleId, wrapped SecretId</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="" align="left" valign="top"><p>Provided</p></td></tr></tbody></table></div></div><br class="table-break"><div class="table"><a name="d0e29324" href="#d0e29324"></a><p class="title"><b>Table&nbsp;100.2.&nbsp;Pull/Push/Wrapped Matrix</b></p><div class="table-contents"><table summary="Pull/Push/Wrapped Matrix" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>RoleId</strong></span></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>SecretId</strong></span></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><span class="strong"><strong>Supported</strong></span></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#9989;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pull</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#9989;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wrapped</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#9989;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Absent</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#9989;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pull</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#9989;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pull</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pull</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#9989;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pull</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wrapped</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#10060;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pull</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Absent</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#10060;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wrapped</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Provided</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#9989;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wrapped</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pull</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#10060;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wrapped</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wrapped</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>&#9989;</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Wrapped</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Absent</p></td><td style="" align="left" valign="top"><p>&#10060;</p></td></tr></tbody></table></div></div><br class="table-break"><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>You can use still all combinations of push/pull/wrapped modes by providing a configured <code class="literal">AppRoleAuthentication</code> bean within the bootstrap context. Spring Cloud Vault cannot derive all possible AppRole combinations from the configuration properties.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>AppRole authentication is limited to simple pull mode using reactive infrastructure. Full pull mode is not yet supported. Using Spring Cloud Vault with the Spring WebFlux stack enables Vault&#8217;s reactive auto-configuration which can be disabled by setting <code class="literal">spring.cloud.vault.reactive.enabled=false</code>.</p></td></tr></table></div><div class="example"><a name="d0e29477" href="#d0e29477"></a><p class="title"><b>Example&nbsp;100.7.&nbsp;bootstrap.yml with all AppRole authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: APPROLE
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> app-role</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role-id</span>: bde2076b-cccb-<span class="hl-number">3</span>cf0-d57e-bca7b1e83a52
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> secret-id</span>: <span class="hl-number">1696536f</span>-<span class="hl-number">1976</span>-<span class="hl-number">73</span>b1-b241-<span class="hl-number">0</span>b4213908d39
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-role
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> app-role-path</span>: approle</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">role-id</code> sets the RoleId.</li><li class="listitem"><code class="literal">secret-id</code> sets the SecretId. SecretId can be omitted if AppRole is configured without requiring SecretId (See <code class="literal">bind_secret_id</code>).</li><li class="listitem"><code class="literal">role</code>: sets the AppRole name for pull mode.</li><li class="listitem"><code class="literal">app-role-path</code> sets the path of the approle authentication mount to use.</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/auth/approle.html" target="_top">Vault Documentation: Using the AppRole auth backend</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.authentication.awsec2" href="#vault.config.authentication.awsec2"></a>100.4&nbsp;AWS-EC2 authentication</h2></div></div></div><p>The <a class="link" href="https://www.vaultproject.io/docs/auth/aws-ec2.html" target="_top">aws-ec2</a>
auth backend provides a secure introduction mechanism
for AWS EC2 instances, allowing automated retrieval of a Vault
token. Unlike most Vault authentication backends, this backend
does not require first-deploying, or provisioning security-sensitive
credentials (tokens, username/password, client certificates, etc.).
Instead, it treats AWS as a Trusted Third Party and uses the
cryptographically signed dynamic metadata information that uniquely
represents each EC2 instance.</p><div class="example"><a name="d0e29518" href="#d0e29518"></a><p class="title"><b>Example&nbsp;100.8.&nbsp;bootstrap.yml using AWS-EC2 Authentication</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: AWS_EC2</pre></div></div><br class="example-break"><p>AWS-EC2 authentication enables nonce by default to follow
the Trust On First Use (TOFU) principle. Any unintended party that
gains access to the PKCS#7 identity metadata can authenticate
against Vault.</p><p>During the first login, Spring Cloud Vault generates a nonce
that is stored in the auth backend aside the instance Id.
Re-authentication requires the same nonce to be sent. Any other
party does not have the nonce and can raise an alert in Vault for
further investigation.</p><p>The nonce is kept in memory and is lost during application restart.
You can configure a static nonce with <code class="literal">spring.cloud.vault.aws-ec2.nonce</code>.</p><p>AWS-EC2 authentication roles are optional and default to the AMI.
You can configure the authentication role by setting the
<code class="literal">spring.cloud.vault.aws-ec2.role</code> property.</p><div class="example"><a name="d0e29537" href="#d0e29537"></a><p class="title"><b>Example&nbsp;100.9.&nbsp;bootstrap.yml with configured role</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: AWS_EC2
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> aws-ec2</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: application-server</pre></div></div><br class="example-break"><div class="example"><a name="d0e29542" href="#d0e29542"></a><p class="title"><b>Example&nbsp;100.10.&nbsp;bootstrap.yml with all AWS EC2 authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: AWS_EC2
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> aws-ec2</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: application-server
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> aws-ec2-path</span>: aws-ec2
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> identity-document</span>: http://...
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> nonce</span>: my-static-nonce</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">authentication</code> setting this value to <code class="literal">AWS_EC2</code> selects the AWS EC2
authentication method</li><li class="listitem"><code class="literal">role</code> sets the name of the role against which the login is being attempted.</li><li class="listitem"><code class="literal">aws-ec2-path</code> sets the path of the AWS EC2 mount to use</li><li class="listitem"><code class="literal">identity-document</code> sets URL of the PKCS#7 AWS EC2 identity document</li><li class="listitem"><code class="literal">nonce</code> used for AWS-EC2 authentication. An empty nonce defaults to nonce generation</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/auth/aws.html" target="_top">Vault Documentation: Using the aws auth backend</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.authentication.awsiam" href="#vault.config.authentication.awsiam"></a>100.5&nbsp;AWS-IAM authentication</h2></div></div></div><p>The <a class="link" href="https://www.vaultproject.io/docs/auth/aws-ec2.html" target="_top">aws</a> backend provides a secure
authentication mechanism for AWS IAM roles, allowing the automatic authentication with
vault based on the current IAM role of the running application.
Unlike most Vault authentication backends, this backend
does not require first-deploying, or provisioning security-sensitive
credentials (tokens, username/password, client certificates, etc.).
Instead, it treats AWS as a Trusted Third Party and uses the
4 pieces of information signed by the caller with their IAM credentials
to verify that the caller is indeed using that IAM role.</p><p>The current IAM role the application is running in is automatically calculated.
If you are running your application on AWS ECS then the application
will use the IAM role assigned to the ECS task of the running container.
If you are running your application naked on top of an EC2 instance then
the IAM role used will be the one assigned to the EC2 instance.</p><p>When using the AWS-IAM authentication you must create a role in Vault
and assign it to your IAM role. An empty <code class="literal">role</code> defaults to
the friendly name the current IAM role.</p><div class="example"><a name="d0e29595" href="#d0e29595"></a><p class="title"><b>Example&nbsp;100.11.&nbsp;bootstrap.yml with required AWS-IAM Authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: AWS_IAM</pre></div></div><br class="example-break"><div class="example"><a name="d0e29600" href="#d0e29600"></a><p class="title"><b>Example&nbsp;100.12.&nbsp;bootstrap.yml with all AWS-IAM Authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: AWS_IAM
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> aws-iam</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-dev-role
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> aws-path</span>: aws
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server-id</span>: some.server.name</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">role</code> sets the name of the role against which the login is being attempted. This should be bound to your IAM role. If one is not supplied then the friendly name of the current IAM user will be used as the vault role.</li><li class="listitem"><code class="literal">aws-path</code> sets the path of the AWS mount to use</li><li class="listitem"><code class="literal">server-id</code> sets the value to use for the <code class="literal">X-Vault-AWS-IAM-Server-ID</code> header preventing certain types of replay attacks.</li></ul></div><p>AWS-IAM requires the AWS Java SDK dependency (<code class="literal">com.amazonaws:aws-java-sdk-core</code>)
as the authentication implementation uses AWS SDK types for credentials and request signing.</p><p>See also: <a class="link" href="https://www.vaultproject.io/docs/auth/aws.html" target="_top">Vault Documentation: Using the aws auth backend</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.authentication.azuremsi" href="#vault.config.authentication.azuremsi"></a>100.6&nbsp;Azure MSI authentication</h2></div></div></div><p>The <a class="link" href="https://www.vaultproject.io/docs/auth/azure.html" target="_top">azure</a>
auth backend provides a secure introduction mechanism
for Azure VM instances, allowing automated retrieval of a Vault
token. Unlike most Vault authentication backends, this backend
does not require first-deploying, or provisioning security-sensitive
credentials (tokens, username/password, client certificates, etc.).
Instead, it treats Azure as a Trusted Third Party and uses the
managed service identity and instance metadata information that can be
bound to a VM instance.</p><div class="example"><a name="d0e29641" href="#d0e29641"></a><p class="title"><b>Example&nbsp;100.13.&nbsp;bootstrap.yml with required Azure Authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: AZURE_MSI
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> azure-msi</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-dev-role</pre></div></div><br class="example-break"><div class="example"><a name="d0e29646" href="#d0e29646"></a><p class="title"><b>Example&nbsp;100.14.&nbsp;bootstrap.yml with all Azure Authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: AZURE_MSI
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> azure-msi</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-dev-role
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> azure-path</span>: aws</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">role</code> sets the name of the role against which the login is being attempted.</li><li class="listitem"><code class="literal">azure-path</code> sets the path of the Azure mount to use</li></ul></div><p>Azure MSI authentication fetches environmental details about the virtual machine
(subscription Id, resource group, VM name) from the instance metadata service.</p><p>See also: <a class="link" href="https://www.vaultproject.io/docs/auth/azure.html" target="_top">Vault Documentation: Using the azure auth backend</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.authentication.clientcert" href="#vault.config.authentication.clientcert"></a>100.7&nbsp;TLS certificate authentication</h2></div></div></div><p>The <code class="literal">cert</code> auth backend allows authentication using SSL/TLS client
certificates that are either signed by a CA or self-signed.</p><p>To enable <code class="literal">cert</code> authentication you need to:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Use SSL, see <a class="xref" href="#vault.config.ssl" title="108.&nbsp;Vault Client SSL configuration">Chapter&nbsp;108, <i>Vault Client SSL configuration</i></a></li><li class="listitem">Configure a Java <code class="literal">Keystore</code> that contains the client
certificate and the private key</li><li class="listitem">Set the <code class="literal">spring.cloud.vault.authentication</code> to <code class="literal">CERT</code></li></ol></div><div class="example"><a name="d0e29700" href="#d0e29700"></a><p class="title"><b>Example&nbsp;100.15.&nbsp;bootstrap.yml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: CERT
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ssl</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key-store</span>: classpath:keystore.jks
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key-store-password</span>: changeit
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cert-auth-path</span>: cert</pre></div></div><br class="example-break"><p>See also: <a class="link" href="https://www.vaultproject.io/docs/auth/cert.html" target="_top">Vault Documentation: Using the Cert auth backend</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.authentication.cubbyhole" href="#vault.config.authentication.cubbyhole"></a>100.8&nbsp;Cubbyhole authentication</h2></div></div></div><p>Cubbyhole authentication uses Vault primitives to provide a secured authentication
workflow. Cubbyhole authentication uses tokens as primary login method.
An ephemeral token is used to obtain a second, login VaultToken from Vault&#8217;s
Cubbyhole secret backend. The login token is usually longer-lived and used to
interact with Vault. The login token will be retrieved from a wrapped
response stored at <code class="literal">/cubbyhole/response</code>.</p><p><span class="strong"><strong>Creating a wrapped token</strong></span></p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Response Wrapping for token creation requires Vault 0.6.0 or higher.</p></td></tr></table></div><div class="example"><a name="d0e29723" href="#d0e29723"></a><p class="title"><b>Example&nbsp;100.16.&nbsp;Creating and storing tokens</b></p><div class="example-contents"><pre class="programlisting">$ vault token-create -wrap-ttl="10m"
Key Value
--- -----
wrapping_token: 397ccb93-ff6c-b17b-9389-380b01ca2645
wrapping_token_ttl: 0h10m0s
wrapping_token_creation_time: 2016-09-18 20:29:48.652957077 +0200 CEST
wrapped_accessor: 46b6aebb-187f-932a-26d7-4f3d86a68319</pre></div></div><br class="example-break"><div class="example"><a name="d0e29728" href="#d0e29728"></a><p class="title"><b>Example&nbsp;100.17.&nbsp;bootstrap.yml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: CUBBYHOLE
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> token</span>: <span class="hl-number">397</span>ccb93-ff6c-b17b-<span class="hl-number">9389</span>-<span class="hl-number">380</span>b01ca2645</pre></div></div><br class="example-break"><p>See also:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://www.vaultproject.io/docs/concepts/tokens.html" target="_top">Vault Documentation: Tokens</a></li><li class="listitem"><a class="link" href="https://www.vaultproject.io/docs/secrets/cubbyhole/index.html" target="_top">Vault Documentation: Cubbyhole Secret Backend</a></li><li class="listitem"><a class="link" href="https://www.vaultproject.io/docs/concepts/response-wrapping.html" target="_top">Vault Documentation: Response Wrapping</a></li></ul></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault.config.authentication.gcpgce" href="#vault.config.authentication.gcpgce"></a>101.&nbsp;GCP-GCE authentication</h2></div></div></div><p>The <a class="link" href="https://www.vaultproject.io/docs/auth/gcp.html" target="_top">gcp</a>
auth backend allows Vault login by using existing GCP (Google Cloud Platform) IAM and GCE credentials.</p><p>GCP GCE (Google Compute Engine) authentication creates a signature in the form of a
JSON Web Token (JWT) for a service account. A JWT for a Compute Engine instance
is obtained from the GCE metadata service using <a class="link" href="https://cloud.google.com/compute/docs/instances/verifying-instance-identity" target="_top">Instance identification</a>.
This API creates a JSON Web Token that can be used to confirm the instance identity.</p><p>Unlike most Vault authentication backends, this backend
does not require first-deploying, or provisioning security-sensitive
credentials (tokens, username/password, client certificates, etc.).
Instead, it treats GCP as a Trusted Third Party and uses the
cryptographically signed dynamic metadata information that uniquely
represents each GCP service account.</p><div class="example"><a name="d0e29763" href="#d0e29763"></a><p class="title"><b>Example&nbsp;101.1.&nbsp;bootstrap.yml with required GCP-GCE Authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: GCP_GCE
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gcp-gce</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-dev-role</pre></div></div><br class="example-break"><div class="example"><a name="d0e29768" href="#d0e29768"></a><p class="title"><b>Example&nbsp;101.2.&nbsp;bootstrap.yml with all GCP-GCE Authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: GCP_GCE
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gcp-gce</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gcp-path</span>: gcp
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-dev-role
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> service-account</span>: my-service@projectid.iam.gserviceaccount.com</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">role</code> sets the name of the role against which the login is being attempted.</li><li class="listitem"><code class="literal">gcp-path</code> sets the path of the GCP mount to use</li><li class="listitem"><code class="literal">service-account</code> allows overriding the service account Id to a specific value. Defaults to the <code class="literal">default</code> service account.</li></ul></div><p>See also:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://www.vaultproject.io/docs/auth/gcp.html" target="_top">Vault Documentation: Using the GCP auth backend</a></li><li class="listitem"><a class="link" href="https://cloud.google.com/compute/docs/instances/verifying-instance-identity" target="_top">GCP Documentation: Verifying the Identity of Instances</a></li></ul></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault.config.authentication.gcpiam" href="#vault.config.authentication.gcpiam"></a>102.&nbsp;GCP-IAM authentication</h2></div></div></div><p>The <a class="link" href="https://www.vaultproject.io/docs/auth/gcp.html" target="_top">gcp</a>
auth backend allows Vault login by using existing GCP (Google Cloud Platform) IAM and GCE credentials.</p><p>GCP IAM authentication creates a signature in the form of a JSON Web Token (JWT)
for a service account. A JWT for a service account is obtained by
calling GCP IAM&#8217;s <a class="link" href="https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signJwt" target="_top"><code class="literal">projects.serviceAccounts.signJwt</code></a> API. The caller authenticates against GCP IAM
and proves thereby its identity. This Vault backend treats GCP as a Trusted Third Party.</p><p>IAM credentials can be obtained from either the runtime environment
, specifically the <a class="link" href="https://cloud.google.com/docs/authentication/production" target="_top"><code class="literal">GOOGLE_APPLICATION_CREDENTIALS</code></a>
environment variable, the Google Compute metadata service,
or supplied externally as e.g. JSON or base64 encoded.
JSON is the preferred form as it carries the project id and
service account identifier required for calling <code class="literal">projects.serviceAccounts.signJwt</code>.</p><div class="example"><a name="d0e29826" href="#d0e29826"></a><p class="title"><b>Example&nbsp;102.1.&nbsp;bootstrap.yml with required GCP-IAM Authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: GCP_IAM
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gcp-iam</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-dev-role</pre></div></div><br class="example-break"><div class="example"><a name="d0e29831" href="#d0e29831"></a><p class="title"><b>Example&nbsp;102.2.&nbsp;bootstrap.yml with all GCP-IAM Authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: GCP_IAM
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gcp-iam</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> credentials</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> location</span>: classpath:credentials.json
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> encoded-key</span>: e+KApn0=
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gcp-path</span>: gcp
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> jwt-validity</span>: <span class="hl-number">15</span>m
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> project-id</span>: my-project-id
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-dev-role
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> service-account</span>: my-service@projectid.iam.gserviceaccount.com</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">role</code> sets the name of the role against which the login is being attempted.</li><li class="listitem"><code class="literal">credentials.location</code> path to the credentials resource that contains Google credentials in JSON format.</li><li class="listitem"><code class="literal">credentials.encoded-key</code> the base64 encoded contents of an OAuth2 account private key in the JSON format.</li><li class="listitem"><code class="literal">gcp-path</code> sets the path of the GCP mount to use</li><li class="listitem"><code class="literal">jwt-validity</code> configures the JWT token validity. Defaults to 15 minutes.</li><li class="listitem"><code class="literal">project-id</code> allows overriding the project Id to a specific value. Defaults to the project Id from the obtained credential.</li><li class="listitem"><code class="literal">service-account</code> allows overriding the service account Id to a specific value. Defaults to the service account from the obtained credential.</li></ul></div><p>GCP IAM authentication requires the Google Cloud Java SDK dependency
(<code class="literal">com.google.apis:google-api-services-iam</code> and <code class="literal">com.google.auth:google-auth-library-oauth2-http</code>)
as the authentication implementation uses Google APIs for credentials and JWT signing.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Google credentials require an OAuth 2 token maintaining the token lifecycle. All API
is synchronous therefore, <code class="literal">GcpIamAuthentication</code> does not support <code class="literal">AuthenticationSteps</code> which is
required for reactive usage.</p></td></tr></table></div><p>See also:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://www.vaultproject.io/docs/auth/gcp.html" target="_top">Vault Documentation: Using the GCP auth backend</a></li><li class="listitem"><a class="link" href="https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signJwt" target="_top">GCP Documentation: projects.serviceAccounts.signJwt</a><a name="vault.authentication.gcpiam" href="#vault.authentication.gcpiam"></a></li></ul></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.authentication.kubernetes" href="#vault.config.authentication.kubernetes"></a>102.1&nbsp;Kubernetes authentication</h2></div></div></div><p>Kubernetes authentication mechanism (since Vault 0.8.3) allows to authenticate with Vault using a Kubernetes Service Account Token.
The authentication is role based and the role is bound to a service account name and a namespace.</p><p>A file containing a JWT token for a pod&#8217;s service account is automatically mounted at <code class="literal">/var/run/secrets/kubernetes.io/serviceaccount/token</code>.</p><div class="example"><a name="d0e29911" href="#d0e29911"></a><p class="title"><b>Example&nbsp;102.3.&nbsp;bootstrap.yml with all Kubernetes authentication properties</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> authentication</span>: KUBERNETES
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> kubernetes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: my-dev-role
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> kubernetes-path</span>: kubernetes
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> service-account-token-file</span>: /var/run/secrets/kubernetes.io/serviceaccount/token</pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">role</code> sets the Role.</li><li class="listitem"><code class="literal">kubernetes-path</code> sets the path of the Kubernetes mount to use.</li><li class="listitem"><code class="literal">service-account-token-file</code> sets the location of the file containing the Kubernetes Service Account Token. Defaults to <code class="literal">/var/run/secrets/kubernetes.io/serviceaccount/token</code>.</li></ul></div><p>See also:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://www.vaultproject.io/docs/auth/kubernetes.html" target="_top">Vault Documentation: Kubernetes</a></li><li class="listitem"><a class="link" href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/" target="_top">Kubernetes Documentation: Configure Service Accounts for Pods</a></li></ul></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault.config.backends" href="#vault.config.backends"></a>103.&nbsp;Secret Backends</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.generic" href="#vault.config.backends.generic"></a>103.1&nbsp;Generic Backend</h2></div></div></div><p>Spring Cloud Vault supports at the basic level the generic secret
backend. The generic secret backend allows storage of arbitrary
values as key-value store. A single context can store one or many
key-value tuples. Contexts can be organized hierarchically.
Spring Cloud Vault allows using the Application name
and a default context name (<code class="literal">application</code>) in combination with active
profiles.</p><pre class="screen">/secret/{application}/{profile}
/secret/{application}
/secret/{default-context}/{profile}
/secret/{default-context}</pre><p>The application name is determined by the properties:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">spring.cloud.vault.generic.application-name</code></li><li class="listitem"><code class="literal">spring.cloud.vault.application-name</code></li><li class="listitem"><code class="literal">spring.application.name</code></li></ul></div><p>Secrets can be obtained from other contexts within the generic backend by adding their
paths to the application name, separated by commas. For example, given the application
name <code class="literal">usefulapp,mysql1,projectx/aws</code>, each of these folders will be used:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">/secret/usefulapp</code></li><li class="listitem"><code class="literal">/secret/mysql1</code></li><li class="listitem"><code class="literal">/secret/projectx/aws</code></li></ul></div><p>Spring Cloud Vault adds all active profiles to the list of possible context paths.
No active profiles will skip accessing contexts with a profile name.</p><p>Properties are exposed like they are stored (i.e. without additional prefixes).</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> generic</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: secret
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profile-separator</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> default-context</span>: application
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application-name</span>: my-app</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">false</code> disables the secret backend
config usage</li><li class="listitem"><code class="literal">backend</code> sets the path of the secret mount to use</li><li class="listitem"><code class="literal">default-context</code> sets the context name used by all applications</li><li class="listitem"><code class="literal">application-name</code> overrides the application name for use in the generic backend</li><li class="listitem"><code class="literal">profile-separator</code> separates the profile name from the context in
property sources with profiles</li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The key-value secret backend can be operated in versioned (v2) and non-versioned (v1) modes. Depending on the mode of operation, a different API is required to access secrets. Make sure to enable <code class="literal">generic</code> secret backend usage for non-versioned key-value backends and <code class="literal">kv</code> secret backend usage for versioned key-value backends.</p></td></tr></table></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/kv/kv-v1.html" target="_top">Vault Documentation: Using the KV Secrets Engine - Version 1 (generic secret backend)</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.kv.versioned" href="#vault.config.backends.kv.versioned"></a>103.2&nbsp;Versioned Key-Value Backend</h2></div></div></div><p>Spring Cloud Vault supports the versioned Key-Value secret
backend. The key-value backend allows storage of arbitrary
values as key-value store. A single context can store one or many
key-value tuples. Contexts can be organized hierarchically.
Spring Cloud Vault allows using the Application name
and a default context name (<code class="literal">application</code>) in combination with active
profiles.</p><pre class="screen">/secret/{application}/{profile}
/secret/{application}
/secret/{default-context}/{profile}
/secret/{default-context}</pre><p>The application name is determined by the properties:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">spring.cloud.vault.kv.application-name</code></li><li class="listitem"><code class="literal">spring.cloud.vault.application-name</code></li><li class="listitem"><code class="literal">spring.application.name</code></li></ul></div><p>Secrets can be obtained from other contexts within the key-value backend by adding their
paths to the application name, separated by commas. For example, given the application
name <code class="literal">usefulapp,mysql1,projectx/aws</code>, each of these folders will be used:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">/secret/usefulapp</code></li><li class="listitem"><code class="literal">/secret/mysql1</code></li><li class="listitem"><code class="literal">/secret/projectx/aws</code></li></ul></div><p>Spring Cloud Vault adds all active profiles to the list of possible context paths.
No active profiles will skip accessing contexts with a profile name.</p><p>Properties are exposed like they are stored (i.e. without additional prefixes).</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Vault adds the <code class="literal">data/</code> context between the mount path and the actual context path.</p></td></tr></table></div><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> kv</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: secret
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profile-separator</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/'</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> default-context</span>: application
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application-name</span>: my-app</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">false</code> disables the secret backend
config usage</li><li class="listitem"><code class="literal">backend</code> sets the path of the secret mount to use</li><li class="listitem"><code class="literal">default-context</code> sets the context name used by all applications</li><li class="listitem"><code class="literal">application-name</code> overrides the application name for use in the generic backend</li><li class="listitem"><code class="literal">profile-separator</code> separates the profile name from the context in
property sources with profiles</li></ul></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The key-value secret backend can be operated in versioned (v2) and non-versioned (v1) modes. Depending on the mode of operation, a different API is required to access secrets. Make sure to enable <code class="literal">generic</code> secret backend usage for non-versioned key-value backends and <code class="literal">kv</code> secret backend usage for versioned key-value backends.</p></td></tr></table></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/kv/kv-v2.html" target="_top">Vault Documentation: Using the KV Secrets Engine - Version 2 (versioned key-value backend)</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.consul" href="#vault.config.backends.consul"></a>103.3&nbsp;Consul</h2></div></div></div><p>Spring Cloud Vault can obtain credentials for HashiCorp Consul.
The Consul integration requires the <code class="literal">spring-cloud-vault-config-consul</code>
dependency.</p><div class="example"><a name="d0e30147" href="#d0e30147"></a><p class="title"><b>Example&nbsp;103.1.&nbsp;pom.xml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-vault-config-consul<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>Greenwich.M3<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span></pre></div></div><br class="example-break"><p>The integration can be enabled by setting
<code class="literal">spring.cloud.vault.consul.enabled=true</code> (default <code class="literal">false</code>) and
providing the role name with <code class="literal">spring.cloud.vault.consul.role=&#8230;</code>.</p><p>The obtained token is stored in <code class="literal">spring.cloud.consul.token</code>
so using Spring Cloud Consul can pick up the generated
credentials without further configuration. You can configure
the property name by setting <code class="literal">spring.cloud.vault.consul.token-property</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> consul</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: readonly
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: consul
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> token-property</span>: spring.cloud.consul.token</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">true</code> enables the Consul backend config usage</li><li class="listitem"><code class="literal">role</code> sets the role name of the Consul role definition</li><li class="listitem"><code class="literal">backend</code> sets the path of the Consul mount to use</li><li class="listitem"><code class="literal">token-property</code> sets the property name in which the Consul ACL token is stored</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/consul/index.html" target="_top">Vault Documentation: Setting up Consul with Vault</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.rabbitmq" href="#vault.config.backends.rabbitmq"></a>103.4&nbsp;RabbitMQ</h2></div></div></div><p>Spring Cloud Vault can obtain credentials for RabbitMQ.</p><p>The RabbitMQ integration requires the <code class="literal">spring-cloud-vault-config-rabbitmq</code>
dependency.</p><div class="example"><a name="d0e30212" href="#d0e30212"></a><p class="title"><b>Example&nbsp;103.2.&nbsp;pom.xml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-vault-config-rabbitmq<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>Greenwich.M3<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span></pre></div></div><br class="example-break"><p>The integration can be enabled by setting
<code class="literal">spring.cloud.vault.rabbitmq.enabled=true</code> (default <code class="literal">false</code>)
and providing the role name with <code class="literal">spring.cloud.vault.rabbitmq.role=&#8230;</code>.</p><p>Username and password are stored in <code class="literal">spring.rabbitmq.username</code>
and <code class="literal">spring.rabbitmq.password</code> so using Spring Boot will pick up the generated
credentials without further configuration. You can configure the property names
by setting <code class="literal">spring.cloud.vault.rabbitmq.username-property</code> and
<code class="literal">spring.cloud.vault.rabbitmq.password-property</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> rabbitmq</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: readonly
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: rabbitmq
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username-property</span>: spring.rabbitmq.username
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password-property</span>: spring.rabbitmq.password</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">true</code> enables the RabbitMQ backend config usage</li><li class="listitem"><code class="literal">role</code> sets the role name of the RabbitMQ role definition</li><li class="listitem"><code class="literal">backend</code> sets the path of the RabbitMQ mount to use</li><li class="listitem"><code class="literal">username-property</code> sets the property name in which the RabbitMQ username is stored</li><li class="listitem"><code class="literal">password-property</code> sets the property name in which the RabbitMQ password is stored</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/rabbitmq/index.html" target="_top">Vault Documentation: Setting up RabbitMQ with Vault</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.aws" href="#vault.config.backends.aws"></a>103.5&nbsp;AWS</h2></div></div></div><p>Spring Cloud Vault can obtain credentials for AWS.</p><p>The AWS integration requires the <code class="literal">spring-cloud-vault-config-aws</code>
dependency.</p><div class="example"><a name="d0e30288" href="#d0e30288"></a><p class="title"><b>Example&nbsp;103.3.&nbsp;pom.xml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-vault-config-aws<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>Greenwich.M3<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span></pre></div></div><br class="example-break"><p>The integration can be enabled by setting
<code class="literal">spring.cloud.vault.aws=true</code> (default <code class="literal">false</code>)
and providing the role name with <code class="literal">spring.cloud.vault.aws.role=&#8230;</code>.</p><p>The access key and secret key are stored in <code class="literal">cloud.aws.credentials.accessKey</code>
and <code class="literal">cloud.aws.credentials.secretKey</code> so using Spring Cloud AWS will pick up the generated
credentials without further configuration. You can configure the property names
by setting <code class="literal">spring.cloud.vault.aws.access-key-property</code> and
<code class="literal">spring.cloud.vault.aws.secret-key-property</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> aws</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: readonly
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: aws
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> access-key-property</span>: cloud.aws.credentials.accessKey
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> secret-key-property</span>: cloud.aws.credentials.secretKey</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">true</code> enables the AWS backend config usage</li><li class="listitem"><code class="literal">role</code> sets the role name of the AWS role definition</li><li class="listitem"><code class="literal">backend</code> sets the path of the AWS mount to use</li><li class="listitem"><code class="literal">access-key-property</code> sets the property name in which the AWS access key is stored</li><li class="listitem"><code class="literal">secret-key-property</code> sets the property name in which the AWS secret key is stored</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/aws/index.html" target="_top">Vault Documentation: Setting up AWS with Vault</a></p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault.config.backends.database-backends" href="#vault.config.backends.database-backends"></a>104.&nbsp;Database backends</h2></div></div></div><p>Vault supports several database secret backends to generate database
credentials dynamically based on configured roles. This means
services that need to access a database no longer need to configure
credentials: they can request them from Vault, and use Vault&#8217;s leasing
mechanism to more easily roll keys.</p><p>Spring Cloud Vault integrates with these backends:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="xref" href="#vault.config.backends.database" title="104.1&nbsp;Database">Section&nbsp;104.1, &#8220;Database&#8221;</a></li><li class="listitem"><a class="xref" href="#vault.config.backends.cassandra" title="104.2&nbsp;Apache Cassandra">Section&nbsp;104.2, &#8220;Apache Cassandra&#8221;</a></li><li class="listitem"><a class="xref" href="#vault.config.backends.mongodb" title="104.3&nbsp;MongoDB">Section&nbsp;104.3, &#8220;MongoDB&#8221;</a></li><li class="listitem"><a class="xref" href="#vault.config.backends.mysql" title="104.4&nbsp;MySQL">Section&nbsp;104.4, &#8220;MySQL&#8221;</a></li><li class="listitem"><a class="xref" href="#vault.config.backends.postgresql" title="104.5&nbsp;PostgreSQL">Section&nbsp;104.5, &#8220;PostgreSQL&#8221;</a></li></ul></div><p>Using a database secret backend requires to enable the
backend in the configuration and the <code class="literal">spring-cloud-vault-config-databases</code>
dependency.</p><p>Vault ships since 0.7.1 with a dedicated <code class="literal">database</code> secret backend that allows
database integration via plugins. You can use that specific backend by using the
generic database backend. Make sure to specify the appropriate
backend path, e.g. <code class="literal">spring.cloud.vault.mysql.role.backend=database</code>.</p><div class="example"><a name="d0e30390" href="#d0e30390"></a><p class="title"><b>Example&nbsp;104.1.&nbsp;pom.xml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-vault-config-databases<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>Greenwich.M3<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span></pre></div></div><br class="example-break"><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Enabling multiple JDBC-compliant databases will generate credentials
and store them by default in the same property keys hence property names for
JDBC secrets need to be configured separately.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.database" href="#vault.config.backends.database"></a>104.1&nbsp;Database</h2></div></div></div><p>Spring Cloud Vault can obtain credentials for any database listed at
<a class="link" href="https://www.vaultproject.io/api/secret/databases/index.html" target="_top">https://www.vaultproject.io/api/secret/databases/index.html</a>.
The integration can be enabled by setting
<code class="literal">spring.cloud.vault.database.enabled=true</code> (default <code class="literal">false</code>) and
providing the role name with <code class="literal">spring.cloud.vault.database.role=&#8230;</code>.</p><p>While the database backend is a generic one, <code class="literal">spring.cloud.vault.database</code>
specifically targets JDBC databases. Username and password are
stored in <code class="literal">spring.datasource.username</code> and <code class="literal">spring.datasource.password</code>
so using Spring Boot will pick up the generated credentials
for your <code class="literal">DataSource</code> without further configuration.
You can configure the property names by setting
<code class="literal">spring.cloud.vault.database.username-property</code> and
<code class="literal">spring.cloud.vault.database.password-property</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> database</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: readonly
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: database
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username-property</span>: spring.datasource.username
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password-property</span>: spring.datasource.username</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">true</code> enables the Database backend config usage</li><li class="listitem"><code class="literal">role</code> sets the role name of the Database role definition</li><li class="listitem"><code class="literal">backend</code> sets the path of the Database mount to use</li><li class="listitem"><code class="literal">username-property</code> sets the property name in which the Database username is stored</li><li class="listitem"><code class="literal">password-property</code> sets the property name in which the Database password is stored</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/databases/index.html" target="_top">Vault Documentation: Database Secrets backend</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.cassandra" href="#vault.config.backends.cassandra"></a>104.2&nbsp;Apache Cassandra</h2></div></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">cassandra</code> backend has been deprecated in Vault 0.7.1 and
it is recommended to use the <code class="literal">database</code> backend and mount it as <code class="literal">cassandra</code>.</p></td></tr></table></div><p>Spring Cloud Vault can obtain credentials for Apache Cassandra.
The integration can be enabled by setting
<code class="literal">spring.cloud.vault.cassandra.enabled=true</code> (default <code class="literal">false</code>) and
providing the role name with <code class="literal">spring.cloud.vault.cassandra.role=&#8230;</code>.</p><p>Username and password are stored in <code class="literal">spring.data.cassandra.username</code>
and <code class="literal">spring.data.cassandra.password</code> so using Spring Boot will pick
up the generated credentials without further configuration.
You can configure the property names by setting
<code class="literal">spring.cloud.vault.cassandra.username-property</code> and
<code class="literal">spring.cloud.vault.cassandra.password-property</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cassandra</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: readonly
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: cassandra
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username-property</span>: spring.data.cassandra.username
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password-property</span>: spring.data.cassandra.username</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">true</code> enables the Cassandra backend config usage</li><li class="listitem"><code class="literal">role</code> sets the role name of the Cassandra role definition</li><li class="listitem"><code class="literal">backend</code> sets the path of the Cassandra mount to use</li><li class="listitem"><code class="literal">username-property</code> sets the property name in which the Cassandra username is stored</li><li class="listitem"><code class="literal">password-property</code> sets the property name in which the Cassandra password is stored</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/cassandra/index.html" target="_top">Vault Documentation: Setting up Apache Cassandra with Vault</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.mongodb" href="#vault.config.backends.mongodb"></a>104.3&nbsp;MongoDB</h2></div></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">mongodb</code> backend has been deprecated in Vault 0.7.1 and
it is recommended to use the <code class="literal">database</code> backend and mount it as <code class="literal">mongodb</code>.</p></td></tr></table></div><p>Spring Cloud Vault can obtain credentials for MongoDB.
The integration can be enabled by setting
<code class="literal">spring.cloud.vault.mongodb.enabled=true</code> (default <code class="literal">false</code>) and
providing the role name with <code class="literal">spring.cloud.vault.mongodb.role=&#8230;</code>.</p><p>Username and password are stored in <code class="literal">spring.data.mongodb.username</code>
and <code class="literal">spring.data.mongodb.password</code> so using Spring Boot will
pick up the generated credentials without further configuration.
You can configure the property names by setting
<code class="literal">spring.cloud.vault.mongodb.username-property</code> and
<code class="literal">spring.cloud.vault.mongodb.password-property</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> mongodb</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: readonly
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: mongodb
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username-property</span>: spring.data.mongodb.username
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password-property</span>: spring.data.mongodb.password</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">true</code> enables the MongodB backend config usage</li><li class="listitem"><code class="literal">role</code> sets the role name of the MongoDB role definition</li><li class="listitem"><code class="literal">backend</code> sets the path of the MongoDB mount to use</li><li class="listitem"><code class="literal">username-property</code> sets the property name in which the MongoDB username is stored</li><li class="listitem"><code class="literal">password-property</code> sets the property name in which the MongoDB password is stored</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/mongodb/index.html" target="_top">Vault Documentation: Setting up MongoDB with Vault</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.mysql" href="#vault.config.backends.mysql"></a>104.4&nbsp;MySQL</h2></div></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">mysql</code> backend has been deprecated in Vault 0.7.1 and
it is recommended to use the <code class="literal">database</code> backend and mount it as <code class="literal">mysql</code>.
Configuration for <code class="literal">spring.cloud.vault.mysql</code> will be removed in a future version.</p></td></tr></table></div><p>Spring Cloud Vault can obtain credentials for MySQL.
The integration can be enabled by setting
<code class="literal">spring.cloud.vault.mysql.enabled=true</code> (default <code class="literal">false</code>) and
providing the role name with <code class="literal">spring.cloud.vault.mysql.role=&#8230;</code>.</p><p>Username and password are stored in <code class="literal">spring.datasource.username</code>
and <code class="literal">spring.datasource.password</code> so using Spring Boot will
pick up the generated credentials without further configuration.
You can configure the property names by setting
<code class="literal">spring.cloud.vault.mysql.username-property</code> and
<code class="literal">spring.cloud.vault.mysql.password-property</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> mysql</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: readonly
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: mysql
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username-property</span>: spring.datasource.username
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password-property</span>: spring.datasource.username</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">true</code> enables the MySQL backend config usage</li><li class="listitem"><code class="literal">role</code> sets the role name of the MySQL role definition</li><li class="listitem"><code class="literal">backend</code> sets the path of the MySQL mount to use</li><li class="listitem"><code class="literal">username-property</code> sets the property name in which the MySQL username is stored</li><li class="listitem"><code class="literal">password-property</code> sets the property name in which the MySQL password is stored</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/mysql/index.html" target="_top">Vault Documentation: Setting up MySQL with Vault</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="vault.config.backends.postgresql" href="#vault.config.backends.postgresql"></a>104.5&nbsp;PostgreSQL</h2></div></div></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The <code class="literal">postgresql</code> backend has been deprecated in Vault 0.7.1 and
it is recommended to use the <code class="literal">database</code> backend and mount it as <code class="literal">postgresql</code>.
Configuration for <code class="literal">spring.cloud.vault.postgresql</code> will be removed in a future version.</p></td></tr></table></div><p>Spring Cloud Vault can obtain credentials for PostgreSQL.
The integration can be enabled by setting
<code class="literal">spring.cloud.vault.postgresql.enabled=true</code> (default <code class="literal">false</code>) and
providing the role name with <code class="literal">spring.cloud.vault.postgresql.role=&#8230;</code>.</p><p>Username and password are stored in <code class="literal">spring.datasource.username</code>
and <code class="literal">spring.datasource.password</code> so using Spring Boot will
pick up the generated credentials without further configuration.
You can configure the property names by setting
<code class="literal">spring.cloud.vault.postgresql.username-property</code> and
<code class="literal">spring.cloud.vault.postgresql.password-property</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> postgresql</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> role</span>: readonly
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> backend</span>: postgresql
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username-property</span>: spring.datasource.username
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password-property</span>: spring.datasource.username</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">enabled</code> setting this value to <code class="literal">true</code> enables the PostgreSQL backend config usage</li><li class="listitem"><code class="literal">role</code> sets the role name of the PostgreSQL role definition</li><li class="listitem"><code class="literal">backend</code> sets the path of the PostgreSQL mount to use</li><li class="listitem"><code class="literal">username-property</code> sets the property name in which the PostgreSQL username is stored</li><li class="listitem"><code class="literal">password-property</code> sets the property name in which the PostgreSQL password is stored</li></ul></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/secrets/postgresql/index.html" target="_top">Vault Documentation: Setting up PostgreSQL with Vault</a></p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault.config.backends.configurer" href="#vault.config.backends.configurer"></a>105.&nbsp;Configure <code class="literal">PropertySourceLocator</code> behavior</h2></div></div></div><p>Spring Cloud Vault uses property-based configuration to create <code class="literal">PropertySource</code>s
for generic and discovered secret backends.</p><p>Discovered backends provide <code class="literal">VaultSecretBackendDescriptor</code> beans to describe the configuration
state to use secret backend as <code class="literal">PropertySource</code>. A <code class="literal">SecretBackendMetadataFactory</code> is required
to create a <code class="literal">SecretBackendMetadata</code> object which contains path, name and property transformation
configuration.</p><p><code class="literal">SecretBackendMetadata</code> is used to back a particular <code class="literal">PropertySource</code>.</p><p>You can register an arbitrary number of beans implementing <code class="literal">VaultConfigurer</code> for customization.
Default generic and discovered backend registration is disabled if Spring Cloud Vault discovers
at least one <code class="literal">VaultConfigurer</code> bean. You can however enable default registration with
<code class="literal">SecretBackendConfigurer.registerDefaultGenericSecretBackends()</code> and <code class="literal">SecretBackendConfigurer.registerDefaultDiscoveredSecretBackends()</code>.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> CustomizationBean <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> VaultConfigurer {
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> addSecretBackends(SecretBackendConfigurer configurer) {
configurer.add(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"secret/my-application"</span>);
configurer.registerDefaultGenericSecretBackends(false);
configurer.registerDefaultDiscoveredSecretBackends(true);
}
}</pre></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>All customization is required to happen in the bootstrap context. Add your configuration
classes to <code class="literal">META-INF/spring.factories</code> at <code class="literal">org.springframework.cloud.bootstrap.BootstrapConfiguration</code>
in your application.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_service_registry_configuration" href="#_service_registry_configuration"></a>106.&nbsp;Service Registry Configuration</h2></div></div></div><p>You can use a <code class="literal">DiscoveryClient</code> (such as from Spring Cloud Consul) to locate
a Vault server by setting spring.cloud.vault.discovery.enabled=true (default <code class="literal">false</code>).
The net result of that is that your apps need a bootstrap.yml (or an environment variable)
with the appropriate discovery configuration.
The benefit is that the Vault can change its co-ordinates, as long as the discovery service
is a fixed point. The default service id is <code class="literal">vault</code> but you can change that on the client with
<code class="literal">spring.cloud.vault.discovery.serviceId</code>.</p><p>The discovery client implementations all support some kind of metadata map
(e.g. for Eureka we have eureka.instance.metadataMap). Some additional properties of the service
may need to be configured in its service registration metadata so that clients can connect
correctly. Service registries that do not provide details about transport layer security
need to provide a <code class="literal">scheme</code> metadata entry to be set either to <code class="literal">https</code> or <code class="literal">http</code>.
If no scheme is configured and the service is not exposed as secure service, then
configuration defaults to <code class="literal">spring.cloud.vault.scheme</code> which is <code class="literal">https</code> when it&#8217;s not set.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault.discovery</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> service-id</span>: my-vault-service</pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault.config.fail-fast" href="#vault.config.fail-fast"></a>107.&nbsp;Vault Client Fail Fast</h2></div></div></div><p>In some cases, it may be desirable to fail startup of a service if
it cannot connect to the Vault Server. If this is the desired
behavior, set the bootstrap configuration property
<code class="literal">spring.cloud.vault.fail-fast=true</code> and the client will halt with
an Exception.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> fail-fast</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault.config.ssl" href="#vault.config.ssl"></a>108.&nbsp;Vault Client SSL configuration</h2></div></div></div><p>SSL can be configured declaratively by setting various properties.
You can set either <code class="literal">javax.net.ssl.trustStore</code> to configure
JVM-wide SSL settings or <code class="literal">spring.cloud.vault.ssl.trust-store</code>
to set SSL settings only for Spring Cloud Vault Config.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ssl</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> trust-store</span>: classpath:keystore.jks
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> trust-store-password</span>: changeit</pre></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">trust-store</code> sets the resource for the trust-store. SSL-secured Vault
communication will validate the Vault SSL certificate with the specified
trust-store.</li><li class="listitem"><code class="literal">trust-store-password</code> sets the trust-store password</li></ul></div><p>Please note that configuring <code class="literal">spring.cloud.vault.ssl.*</code> can be only
applied when either Apache Http Components or the OkHttp client
is on your class-path.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="vault-lease-renewal" href="#vault-lease-renewal"></a>109.&nbsp;Lease lifecycle management (renewal and revocation)</h2></div></div></div><p>With every secret, Vault creates a lease:
metadata containing information such as a time duration,
renewability, and more.</p><p>Vault promises that the data will be valid for the given duration,
or Time To Live (TTL). Once the lease is expired, Vault can
revoke the data, and the consumer of the secret can no longer
be certain that it is valid.</p><p>Spring Cloud Vault maintains a lease lifecycle beyond
the creation of login tokens and secrets. That said,
login tokens and secrets associated with a lease
are scheduled for renewal just before the lease expires
until terminal expiry.
Application shutdown revokes obtained login tokens and renewable
leases.</p><p>Secret service and database backends (such as MongoDB or MySQL)
usually generate a renewable lease so generated credentials will
be disabled on application shutdown.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Static tokens are not renewed or revoked.</p></td></tr></table></div><p>Lease renewal and revocation is enabled by default and can
be disabled by setting <code class="literal">spring.cloud.vault.config.lifecycle.enabled</code>
to <code class="literal">false</code>. This is not recommended as leases can expire and
Spring Cloud Vault cannot longer access Vault or services
using generated credentials and valid credentials remain active
after application shutdown.</p><div class="informalexample"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config.lifecycle.enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre></div><p>See also: <a class="link" href="https://www.vaultproject.io/docs/concepts/lease.html" target="_top">Vault Documentation: Lease, Renew, and Revoke</a></p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_gateway" href="#_spring_cloud_gateway"></a>Part&nbsp;XV.&nbsp;Spring Cloud Gateway</h1></div></div></div><div class="partintro"><div></div><p><span class="strong"><strong>Greenwich.M3</strong></span></p><p>This project provides an API Gateway built on top of the Spring Ecosystem, including: Spring 5, Spring Boot 2 and Project Reactor. Spring Cloud Gateway aims to provide a simple, yet effective way to route to APIs and provide cross cutting concerns to them such as: security, monitoring/metrics, and resiliency.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="gateway-starter" href="#gateway-starter"></a>110.&nbsp;How to Include Spring Cloud Gateway</h2></div></div></div><p>To include Spring Cloud Gateway in your project use the starter with group <code class="literal">org.springframework.cloud</code>
and artifact id <code class="literal">spring-cloud-starter-gateway</code>. See the <a class="link" href="http://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a>
for details on setting up your build system with the current Spring Cloud Release Train.</p><p>If you include the starter, but, for some reason, you do not want the gateway to be enabled, set <code class="literal">spring.cloud.gateway.enabled=false</code>.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Spring Cloud Gateway requires the Netty runtime provided by Spring Boot and Spring Webflux. It does not work in a traditional Servlet Container or built as a WAR.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_glossary" href="#_glossary"></a>111.&nbsp;Glossary</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><span class="strong"><strong>Route</strong></span>: Route the basic building block of the gateway. It is defined by an ID, a destination URI, a collection of predicates and a collection of filters. A route is matched if aggregate predicate is true.</li><li class="listitem"><span class="strong"><strong>Predicate</strong></span>: This is a <a class="link" href="http://docs.oracle.com/javase/8/docs/api/java/util/function/Predicate.html" target="_top">Java 8 Function Predicate</a>. The input type is a <a class="link" href="http://docs.spring.io/spring/docs/5.0.x/javadoc-api/org/springframework/web/server/ServerWebExchange.html" target="_top">Spring Framework <code class="literal">ServerWebExchange</code></a>. This allows developers to match on anything from the HTTP request, such as headers or parameters.</li><li class="listitem"><span class="strong"><strong>Filter</strong></span>: These are instances <a class="link" href="http://docs.spring.io/spring/docs/5.0.x/javadoc-api/org/springframework/web/server/GatewayFilter.html" target="_top">Spring Framework <code class="literal">GatewayFilter</code></a> constructed in with a specific factory. Here, requests and responses can be modified before or after sending the downstream request.</li></ul></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="gateway-how-it-works" href="#gateway-how-it-works"></a>112.&nbsp;How It Works</h2></div></div></div><div class="informalfigure"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-gateway/master/docs/src/main/asciidoc/images/spring_cloud_gateway_diagram.png" alt="Spring Cloud Gateway Diagram"></div></div><p>Clients make requests to Spring Cloud Gateway. If the Gateway Handler Mapping determines that a request matches a Route, it is sent to the Gateway Web Handler. This handler runs sends the request through a filter chain that is specific to the request. The reason the filters are divided by the dotted line, is that filters may execute logic before the proxy request is sent or after. All "pre" filter logic is executed, then the proxy request is made. After the proxy request is made, the "post" filter logic is executed.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>URIs defined in routes without a port will get a default port set to 80 and 443 for HTTP and HTTPS URIs respectively.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="gateway-request-predicates-factories" href="#gateway-request-predicates-factories"></a>113.&nbsp;Route Predicate Factories</h2></div></div></div><p>Spring Cloud Gateway matches routes as part of the Spring WebFlux <code class="literal">HandlerMapping</code> infrastructure. Spring Cloud Gateway includes many built-in Route Predicate Factories. All of these predicates match on different attributes of the HTTP request. Multiple Route Predicate Factories can be combined and are combined via logical <code class="literal">and</code>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_after_route_predicate_factory" href="#_after_route_predicate_factory"></a>113.1&nbsp;After Route Predicate Factory</h2></div></div></div><p>The After Route Predicate Factory takes one parameter, a datetime. This predicate matches requests that happen after the current datetime.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: after_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - After</span>=<span class="hl-number">2017</span>-<span class="hl-number">01</span>-<span class="hl-number">20</span>T17:<span class="hl-number">42</span>:<span class="hl-number">47.789</span>-<span class="hl-number">07</span>:<span class="hl-number">00</span>[America/Denver<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span></pre><p>
</p><p>This route matches any request after Jan 20, 2017 17:42 Mountain Time (Denver).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_before_route_predicate_factory" href="#_before_route_predicate_factory"></a>113.2&nbsp;Before Route Predicate Factory</h2></div></div></div><p>The Before Route Predicate Factory takes one parameter, a datetime. This predicate matches requests that happen before the current datetime.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: before_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Before</span>=<span class="hl-number">2017</span>-<span class="hl-number">01</span>-<span class="hl-number">20</span>T17:<span class="hl-number">42</span>:<span class="hl-number">47.789</span>-<span class="hl-number">07</span>:<span class="hl-number">00</span>[America/Denver<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span></pre><p>
</p><p>This route matches any request before Jan 20, 2017 17:42 Mountain Time (Denver).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_between_route_predicate_factory" href="#_between_route_predicate_factory"></a>113.3&nbsp;Between Route Predicate Factory</h2></div></div></div><p>The Between Route Predicate Factory takes two parameters, datetime1 and datetime2. This predicate matches requests that happen after datetime1 and before datetime2. The datetime2 parameter must be after datetime1.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: between_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Between</span>=<span class="hl-number">2017</span>-<span class="hl-number">01</span>-<span class="hl-number">20</span>T17:<span class="hl-number">42</span>:<span class="hl-number">47.789</span>-<span class="hl-number">07</span>:<span class="hl-number">00</span>[America/Denver]<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> <span class="hl-number">2017</span>-<span class="hl-number">01</span>-<span class="hl-number">21</span>T17:<span class="hl-number">42</span>:<span class="hl-number">47.789</span>-<span class="hl-number">07</span>:<span class="hl-number">00</span>[America/Denver<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span></pre><p>
</p><p>This route matches any request after Jan 20, 2017 17:42 Mountain Time (Denver) and before Jan 21, 2017 17:42 Mountain Time (Denver). This could be useful for maintenance windows.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_cookie_route_predicate_factory" href="#_cookie_route_predicate_factory"></a>113.4&nbsp;Cookie Route Predicate Factory</h2></div></div></div><p>The Cookie Route Predicate Factory takes two parameters, the cookie name and a regular expression. This predicate matches cookies that have the given name and the value matches the regular expression.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: cookie_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Cookie</span>=chocolate<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> ch.p</pre><p>
</p><p>This route matches the request has a cookie named <code class="literal">chocolate</code> who&#8217;s value matches the <code class="literal">ch.p</code> regular expression.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_header_route_predicate_factory" href="#_header_route_predicate_factory"></a>113.5&nbsp;Header Route Predicate Factory</h2></div></div></div><p>The Header Route Predicate Factory takes two parameters, the header name and a regular expression. This predicate matches with a header that has the given name and the value matches the regular expression.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: header_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Header</span>=X-Request-Id<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> \d+</pre><p>
</p><p>This route matches if the request has a header named <code class="literal">X-Request-Id</code> whos value matches the <code class="literal">\d+</code> regular expression (has a value of one or more digits).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_host_route_predicate_factory" href="#_host_route_predicate_factory"></a>113.6&nbsp;Host Route Predicate Factory</h2></div></div></div><p>The Host Route Predicate Factory takes one parameter: the host name pattern. The pattern is an Ant style pattern with <code class="literal">.</code> as the separator. This predicates matches the <code class="literal">Host</code> header that matches the pattern.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: host_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Host</span>=**.somehost.org</pre><p>
</p><p>This route would match if the request has a <code class="literal">Host</code> header has the value <code class="literal">www.somehost.org</code> or <code class="literal">beta.somehost.org</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_method_route_predicate_factory" href="#_method_route_predicate_factory"></a>113.7&nbsp;Method Route Predicate Factory</h2></div></div></div><p>The Method Route Predicate Factory takes one parameter: the HTTP method to match.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: method_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Method</span>=GET</pre><p>
</p><p>This route would match if the request method was a <code class="literal">GET</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_path_route_predicate_factory" href="#_path_route_predicate_factory"></a>113.8&nbsp;Path Route Predicate Factory</h2></div></div></div><p>The Path Route Predicate Factory takes one parameter: a Spring <code class="literal">PathMatcher</code> pattern.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: host_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/foo/{segment<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
</p><p>This route would match if the request path was, for example: <code class="literal">/foo/1</code> or <code class="literal">/foo/bar</code>.</p><p>This predicate extracts the URI template variables (like <code class="literal">segment</code> defined in the example above) as a map of names and values and places it in the <code class="literal">ServerWebExchange.getAttributes()</code> with a key defined in <code class="literal">PathRoutePredicate.URL_PREDICATE_VARS_ATTR</code>. Those values are then available for use by <a class="link" href="#gateway-route-filters">GatewayFilter Factories</a></p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_query_route_predicate_factory" href="#_query_route_predicate_factory"></a>113.9&nbsp;Query Route Predicate Factory</h2></div></div></div><p>The Query Route Predicate Factory takes two parameters: a required <code class="literal">param</code> and an optional <code class="literal">regexp</code>.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: query_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Query</span>=baz</pre><p>
</p><p>This route would match if the request contained a <code class="literal">baz</code> query parameter.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: query_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Query</span>=foo<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> ba.</pre><p>
</p><p>This route would match if the request contained a <code class="literal">foo</code> query parameter whose value matched the <code class="literal">ba.</code> regexp, so <code class="literal">bar</code> and <code class="literal">baz</code> would match.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_remoteaddr_route_predicate_factory" href="#_remoteaddr_route_predicate_factory"></a>113.10&nbsp;RemoteAddr Route Predicate Factory</h2></div></div></div><p>The RemoteAddr Route Predicate Factory takes a list (min size 1) of CIDR-notation (IPv4 or IPv6) strings, e.g. <code class="literal">192.168.0.1/16</code> (where <code class="literal">192.168.0.1</code> is an IP address and <code class="literal">16</code> is a subnet mask).</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: remoteaddr_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - RemoteAddr</span>=<span class="hl-number">192.168</span>.<span class="hl-number">1.1</span>/<span class="hl-number">24</span></pre><p>
</p><p>This route would match if the remote address of the request was, for example, <code class="literal">192.168.1.10</code>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_modifying_the_way_remote_addresses_are_resolved" href="#_modifying_the_way_remote_addresses_are_resolved"></a>113.10.1&nbsp;Modifying the way remote addresses are resolved</h3></div></div></div><p>By default the RemoteAddr Route Predicate Factory uses the remote address from the incoming request.
This may not match the actual client IP address if Spring Cloud Gateway sits behind a proxy layer.</p><p>You can customize the way that the remote address is resolved by setting a custom <code class="literal">RemoteAddressResolver</code>.
Spring Cloud Gateway comes with one non-default remote address resolver which is based off of the <a class="link" href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-For" target="_top">X-Forwarded-For header</a>, <code class="literal">XForwardedRemoteAddressResolver</code>.</p><p><code class="literal">XForwardedRemoteAddressResolver</code> has two static constructor methods which take different approaches to security:</p><p><code class="literal">XForwardedRemoteAddressResolver::trustAll</code> returns a <code class="literal">RemoteAddressResolver</code> which always takes the first IP address found in the <code class="literal">X-Forwarded-For</code> header.
This approach is vulnerable to spoofing, as a malicious client could set an initial value for the <code class="literal">X-Forwarded-For</code> which would be accepted by the resolver.</p><p><code class="literal">XForwardedRemoteAddressResolver::maxTrustedIndex</code> takes an index which correlates to the number of trusted infrastructure running in front of Spring Cloud Gateway.
If Spring Cloud Gateway is, for example only accessible via HAProxy, then a value of 1 should be used.
If two hops of trusted infrastructure are required before Spring Cloud Gateway is accessible, then a value of 2 should be used.</p><p>Given the following header value:</p><pre class="screen">X-Forwarded-For: 0.0.0.1, 0.0.0.2, 0.0.0.3</pre><p>The <code class="literal">maxTrustedIndex</code> values below will yield the following remote addresses.</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><code class="literal">maxTrustedIndex</code></th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">result</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[<code class="literal">Integer.MIN_VALUE</code>,0]</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>(invalid, <code class="literal">IllegalArgumentException</code> during initialization)</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0.0.0.3</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0.0.0.2</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>3</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0.0.0.1</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>[4, <code class="literal">Integer.MAX_VALUE</code>]</p></td><td style="" align="left" valign="top"><p>0.0.0.1</p></td></tr></tbody></table></div><p><a name="gateway-route-filters" href="#gateway-route-filters"></a>Using Java config:</p><p>GatewayConfig.java</p><pre class="programlisting">RemoteAddressResolver resolver = XForwardedRemoteAddressResolver
.maxTrustedIndex(<span class="hl-number">1</span>);
...
.route(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"direct-route"</span>,
r -&gt; r.remoteAddr(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"10.1.1.1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"10.10.1.1/24"</span>)
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://downstream1"</span>)
.route(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"proxied-route"</span>,
r -&gt; r.remoteAddr(resolver, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"10.10.1.1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"10.10.1.1/24"</span>)
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://downstream2"</span>)
)</pre></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_gatewayfilter_factories" href="#_gatewayfilter_factories"></a>114.&nbsp;GatewayFilter Factories</h2></div></div></div><p>Route filters allow the modification of the incoming HTTP request or outgoing HTTP response in some manner. Route filters are scoped to a particular route. Spring Cloud Gateway includes many built-in GatewayFilter Factories.</p><p>NOTE For more detailed examples on how to use any of the following filters, take a look at the <a class="link" href="https://github.com/spring-cloud/spring-cloud-gateway/tree/master/spring-cloud-gateway-core/src/test/java/org/springframework/cloud/gateway/filter/factory" target="_top">unit tests</a>.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_addrequestheader_gatewayfilter_factory" href="#_addrequestheader_gatewayfilter_factory"></a>114.1&nbsp;AddRequestHeader GatewayFilter Factory</h2></div></div></div><p>The AddRequestHeader GatewayFilter Factory takes a name and value parameter.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: add_request_header_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - AddRequestHeader</span>=X-Request-Foo<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> Bar</pre><p>
</p><p>This will add <code class="literal">X-Request-Foo:Bar</code> header to the downstream request&#8217;s headers for all matching requests.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_addrequestparameter_gatewayfilter_factory" href="#_addrequestparameter_gatewayfilter_factory"></a>114.2&nbsp;AddRequestParameter GatewayFilter Factory</h2></div></div></div><p>The AddRequestParameter GatewayFilter Factory takes a name and value parameter.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: add_request_parameter_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - AddRequestParameter</span>=foo<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> bar</pre><p>
</p><p>This will add <code class="literal">foo=bar</code> to the downstream request&#8217;s query string for all matching requests.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_addresponseheader_gatewayfilter_factory" href="#_addresponseheader_gatewayfilter_factory"></a>114.3&nbsp;AddResponseHeader GatewayFilter Factory</h2></div></div></div><p>The AddResponseHeader GatewayFilter Factory takes a name and value parameter.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: add_request_header_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - AddResponseHeader</span>=X-Response-Foo<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> Bar</pre><p>
</p><p>This will add <code class="literal">X-Response-Foo:Bar</code> header to the downstream response&#8217;s headers for all matching requests.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="hystrix" href="#hystrix"></a>114.4&nbsp;Hystrix GatewayFilter Factory</h2></div></div></div><p><a class="link" href="https://github.com/Netflix/Hystrix" target="_top">Hystrix</a> is a library from Netflix that implements the <a class="link" href="https://martinfowler.com/bliki/CircuitBreaker.html" target="_top">circuit breaker pattern</a>.
The Hystrix GatewayFilter allows you to introduce circuit breakers to your gateway routes, protecting your services from cascading failures and allowing you to provide fallback responses in the event of downstream failures.</p><p>To enable Hystrix GatewayFilters in your project, add a dependency on <code class="literal">spring-cloud-starter-netflix-hystrix</code> from <a class="link" href="http://cloud.spring.io/spring-cloud-netflix/" target="_top">Spring Cloud Netflix</a>.</p><p>The Hystrix GatewayFilter Factory requires a single <code class="literal">name</code> parameter, which is the name of the <code class="literal">HystrixCommand</code>.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: hystrix_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Hystrix</span>=myCommandName</pre><p>
</p><p>This wraps the remaining filters in a <code class="literal">HystrixCommand</code> with command name <code class="literal">myCommandName</code>.</p><p>The Hystrix filter can also accept an optional <code class="literal">fallbackUri</code> parameter. Currently, only <code class="literal">forward:</code> schemed URIs are supported. If the fallback is called, the request will be forwarded to the controller matched by the URI.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: hystrix_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: lb://backing-service:<span class="hl-number">8088</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/consumingserviceendpoint
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: Hystrix
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: fallbackcmd
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> fallbackUri</span>: forward:/incaseoffailureusethis
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - RewritePath</span>=/consumingserviceendpoint<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> /backingserviceendpoint</pre><p>
</p><p>This will forward to the <code class="literal">/incaseoffailureusethis</code> URI when the Hystrix fallback is called. Note that this example also demonstrates (optional) Spring Cloud Netflix Ribbon load-balancing via the <code class="literal">lb</code> prefix on the destination URI.</p><p>The primary scenario is to use the <code class="literal">fallbackUri</code> to an internal controller or handler within the gateway app.
However, it is also possible to reroute the request to a controller or handler in an external application, like so:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: ingredients
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: lb://ingredients
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=//ingredients/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: Hystrix
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: fetchIngredients
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> fallbackUri</span>: forward:/fallback
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: ingredients-fallback
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://localhost:<span class="hl-number">9994</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/fallback</pre><p>
</p><p>In this example, there is no <code class="literal">fallback</code> endpoint or handler in the gateway application, however, there is one in another
app, registered under <code class="literal"><a class="link" href="http://localhost:9994" target="_top">http://localhost:9994</a></code>.</p><p>In case of the request being forwarded to fallback, the Hystrix Gateway filter also provides the <code class="literal">Throwable</code> that has
caused it. It&#8217;s added to the <code class="literal">ServerWebExchange</code> as the
<code class="literal">ServerWebExchangeUtils.HYSTRIX_EXECUTION_EXCEPTION_ATTR</code> attribute that can be used when
handling the fallback within the gateway app.</p><p>For the external controller/ handler scenario, headers can be added with exception details. You can find more information
on it in the <a class="link" href="#fallback-headers" title="114.5&nbsp;FallbackHeaders GatewayFilter Factory">FallbackHeaders GatewayFilter Factory section</a>.</p><p>Hystrix settings (such as timeouts) can be configured with global defaults or on a route by route basis using application properties as explained on the <a class="link" href="https://github.com/Netflix/Hystrix/wiki/Configuration" target="_top">Hystrix wiki</a>.</p><p>To set a 5 second timeout for the example route above, the following configuration would be used:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds</span>: <span class="hl-number">5000</span></pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="fallback-headers" href="#fallback-headers"></a>114.5&nbsp;FallbackHeaders GatewayFilter Factory</h2></div></div></div><p>The <code class="literal">FallbackHeaders</code> factory allows you to add Hystrix execution exception details in headers of a request forwarded to
a <code class="literal">fallbackUri</code> in an external application, like in the following scenario:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: ingredients
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: lb://ingredients
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=//ingredients/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: Hystrix
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: fetchIngredients
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> fallbackUri</span>: forward:/fallback
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: ingredients-fallback
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://localhost:<span class="hl-number">9994</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/fallback
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: FallbackHeaders
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> executionExceptionTypeHeaderName</span>: Test-Header</pre><p>
</p><p>In this example, after an execution exception occurs while running the <code class="literal">HystrixCommand</code>, the request will be forwarde to
the <code class="literal">fallback</code> endpoint or handler in an app running on <code class="literal">localhost:9994</code>. The headers with the exception type, message
and -if available- root cause exception type and message will be added to that request by the <code class="literal">FallbackHeaders</code> filter.</p><p>The names of the headers can be overwritten in the config by setting the values of the arguments listed below, along with
their default values:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">executionExceptionTypeHeaderName</code> (<code class="literal">"Execution-Exception-Type"</code>)</li><li class="listitem"><code class="literal">executionExceptionMessageHeaderName</code> (<code class="literal">"Execution-Exception-Message"</code>)</li><li class="listitem"><code class="literal">rootCauseExceptionTypeHeaderName</code> (<code class="literal">"Root-Cause-Exception-Type"</code>)</li><li class="listitem"><code class="literal">rootCauseExceptionMessageHeaderName</code> (<code class="literal">"Root-Cause-Exception-Message"</code>)</li></ul></div><p>You can find more information on how Hystrix works with Gateway in the <a class="link" href="#hystrix" title="114.4&nbsp;Hystrix GatewayFilter Factory">Hystrix GatewayFilter Factory section</a>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_prefixpath_gatewayfilter_factory" href="#_prefixpath_gatewayfilter_factory"></a>114.6&nbsp;PrefixPath GatewayFilter Factory</h2></div></div></div><p>The PrefixPath GatewayFilter Factory takes a single <code class="literal">prefix</code> parameter.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: prefixpath_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - PrefixPath</span>=/mypath</pre><p>
</p><p>This will prefix <code class="literal">/mypath</code> to the path of all matching requests. So a request to <code class="literal">/hello</code>, would be sent to <code class="literal">/mypath/hello</code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_preservehostheader_gatewayfilter_factory" href="#_preservehostheader_gatewayfilter_factory"></a>114.7&nbsp;PreserveHostHeader GatewayFilter Factory</h2></div></div></div><p>The PreserveHostHeader GatewayFilter Factory has not parameters. This filter, sets a request attribute that the routing filter will inspect to determine if the original host header should be sent, rather than the host header determined by the http client.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: preserve_host_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
- PreserveHostHeader</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_requestratelimiter_gatewayfilter_factory" href="#_requestratelimiter_gatewayfilter_factory"></a>114.8&nbsp;RequestRateLimiter GatewayFilter Factory</h2></div></div></div><p>The RequestRateLimiter GatewayFilter Factory is uses a <code class="literal">RateLimiter</code> implementation to determine if the current request is allowed to proceed. If it is not, a status of <code class="literal">HTTP 429 - Too Many Requests</code> (by default) is returned.</p><p>This filter takes an optional <code class="literal">keyResolver</code> parameter and parameters specific to the rate limiter (see below).</p><p><code class="literal">keyResolver</code> is a bean that implements the <code class="literal">KeyResolver</code> interface. In configuration, reference the bean by name using SpEL. <code class="literal">#{@myKeyResolver}</code> is a SpEL expression referencing a bean with the name <code class="literal">myKeyResolver</code>.</p><p><b>KeyResolver.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> KeyResolver {
Mono&lt;String&gt; resolve(ServerWebExchange exchange);
}</pre><p>
</p><p>The <code class="literal">KeyResolver</code> interface allows pluggable strategies to derive the key for limiting requests. In future milestones, there will be some <code class="literal">KeyResolver</code> implementations.</p><p>The default implementation of <code class="literal">KeyResolver</code> is the <code class="literal">PrincipalNameKeyResolver</code> which retrieves the <code class="literal">Principal</code> from the <code class="literal">ServerWebExchange</code> and calls <code class="literal">Principal.getName()</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The RequestRateLimiter is not configurable via the "shortcut" notation. The example below is <span class="emphasis"><em>invalid</em></span></p></td></tr></table></div><p><b>application.properties.&nbsp;</b>
</p><pre class="screen"># INVALID SHORTCUT CONFIGURATION
spring.cloud.gateway.routes[0].filters[0]=RequestRateLimiter=2, 2, #{@userkeyresolver}</pre><p>
</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_redis_ratelimiter" href="#_redis_ratelimiter"></a>114.8.1&nbsp;Redis RateLimiter</h3></div></div></div><p>The redis implementation is based off of work done at <a class="link" href="https://stripe.com/blog/rate-limiters" target="_top">Stripe</a>. It requires the use of the <code class="literal">spring-boot-starter-data-redis-reactive</code> Spring Boot starter.</p><p>The algorithm used is the <a class="link" href="https://en.wikipedia.org/wiki/Token_bucket" target="_top">Token Bucket Algorithm</a>.</p><p>The <code class="literal">redis-rate-limiter.replenishRate</code> is how many requests per second do you want a user to be allowed to do, without any dropped requests. This is the rate that the token bucket is filled.</p><p>The <code class="literal">redis-rate-limiter.burstCapacity</code> is the maximum number of requests a user is allowed to do in a single second. This is the number of tokens the token bucket can hold. Setting this value to zero will block all requests.</p><p>A steady rate is accomplished by setting the same value in <code class="literal">replenishRate</code> and <code class="literal">burstCapacity</code>. Temporary bursts can be allowed by setting <code class="literal">burstCapacity</code> higher than <code class="literal">replenishRate</code>. In this case, the rate limiter needs to be allowed some time between bursts (according to <code class="literal">replenishRate</code>), as 2 consecutive bursts will result in dropped requests (<code class="literal">HTTP 429 - Too Many Requests</code>).</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: requestratelimiter_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: RequestRateLimiter
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> redis-rate-limiter.replenishRate</span>: <span class="hl-number">10</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> redis-rate-limiter.burstCapacity</span>: <span class="hl-number">20</span></pre><p>
</p><p><b>Config.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
KeyResolver userKeyResolver() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> exchange -&gt; Mono.just(exchange.getRequest().getQueryParams().getFirst(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"user"</span>));
}</pre><p>
</p><p>This defines a request rate limit of 10 per user. A burst of 20 is allowed, but the next second only 10 requests will be available. The <code class="literal">KeyResolver</code> is a simple one that gets the <code class="literal">user</code> request parameter (note: this is not recommended for production).</p><p>A rate limiter can also be defined as a bean implementing the <code class="literal">RateLimiter</code> interface. In configuration, reference the bean by name using SpEL. <code class="literal">#{@myRateLimiter}</code> is a SpEL expression referencing a bean with the name <code class="literal">myRateLimiter</code>.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: requestratelimiter_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: RequestRateLimiter
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> rate-limiter</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"#{@myRateLimiter}"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key-resolver</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"#{@userKeyResolver}"</span></pre><p>
</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_redirectto_gatewayfilter_factory" href="#_redirectto_gatewayfilter_factory"></a>114.9&nbsp;RedirectTo GatewayFilter Factory</h2></div></div></div><p>The RedirectTo GatewayFilter Factory takes a <code class="literal">status</code> and a <code class="literal">url</code> parameter. The status should be a 300 series redirect http code, such as 301. The url should be a valid url. This will be the value of the <code class="literal">Location</code> header.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: prefixpath_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - RedirectTo</span>=<span class="hl-number">302</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> http://acme.org</pre><p>
</p><p>This will send a status 302 with a <code class="literal">Location:http://acme.org</code> header to perform a redirect.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_removenonproxyheaders_gatewayfilter_factory" href="#_removenonproxyheaders_gatewayfilter_factory"></a>114.10&nbsp;RemoveNonProxyHeaders GatewayFilter Factory</h2></div></div></div><p>The RemoveNonProxyHeaders GatewayFilter Factory removes headers from forwarded requests. The default list of headers that is removed comes from the <a class="link" href="https://tools.ietf.org/html/draft-ietf-httpbis-p1-messaging-14#section-7.1.3" target="_top">IETF</a>.</p><div class="itemizedlist"><p class="title"><b>The default removed headers are:</b></p><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Connection</li><li class="listitem">Keep-Alive</li><li class="listitem">Proxy-Authenticate</li><li class="listitem">Proxy-Authorization</li><li class="listitem">TE</li><li class="listitem">Trailer</li><li class="listitem">Transfer-Encoding</li><li class="listitem">Upgrade</li></ul></div><p>To change this, set the <code class="literal">spring.cloud.gateway.filter.remove-non-proxy-headers.headers</code> property to the list of header names to remove.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_removerequestheader_gatewayfilter_factory" href="#_removerequestheader_gatewayfilter_factory"></a>114.11&nbsp;RemoveRequestHeader GatewayFilter Factory</h2></div></div></div><p>The RemoveRequestHeader GatewayFilter Factory takes a <code class="literal">name</code> parameter. It is the name of the header to be removed.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: removerequestheader_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - RemoveRequestHeader</span>=X-Request-Foo</pre><p>
</p><p>This will remove the <code class="literal">X-Request-Foo</code> header before it is sent downstream.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_removeresponseheader_gatewayfilter_factory" href="#_removeresponseheader_gatewayfilter_factory"></a>114.12&nbsp;RemoveResponseHeader GatewayFilter Factory</h2></div></div></div><p>The RemoveResponseHeader GatewayFilter Factory takes a <code class="literal">name</code> parameter. It is the name of the header to be removed.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: removeresponseheader_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - RemoveResponseHeader</span>=X-Response-Foo</pre><p>
</p><p>This will remove the <code class="literal">X-Response-Foo</code> header from the response before it is returned to the gateway client.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_rewritepath_gatewayfilter_factory" href="#_rewritepath_gatewayfilter_factory"></a>114.13&nbsp;RewritePath GatewayFilter Factory</h2></div></div></div><p>The RewritePath GatewayFilter Factory takes a path <code class="literal">regexp</code> parameter and a <code class="literal">replacement</code> parameter. This uses Java regular expressions for a flexible way to rewrite the request path.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: rewritepath_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/foo/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - RewritePath</span>=/foo/(?&lt;segment&gt;.*)<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> /$\{segment<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
</p><p>For a request path of <code class="literal">/foo/bar</code>, this will set the path to <code class="literal">/bar</code> before making the downstream request. Notice the <code class="literal">$\</code> which is replaced with <code class="literal">$</code> because of the YAML spec.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_rewriteresponseheader_gatewayfilter_factory" href="#_rewriteresponseheader_gatewayfilter_factory"></a>114.14&nbsp;RewriteResponseHeader GatewayFilter Factory</h2></div></div></div><p>The RewriteResponseHeader GatewayFilter Factory takes <code class="literal">name</code>, <code class="literal">regexp</code>, and <code class="literal">replacement</code> parameters. It uses Java regular expressions for a flexible way to rewrite the response header value.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: rewriteresponseheader_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - RewriteResponseHeader</span>=X-Response-Foo<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> password=[^&amp;]+<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> password=***</pre><p>
</p><p>For a header value of <code class="literal">/42?user=ford&amp;password=omg!what&amp;flag=true</code>, it will be set to <code class="literal">/42?user=ford&amp;password=***&amp;flag=true</code> after making the downstream request. Please use <code class="literal">$\</code> to mean <code class="literal">$</code> because of the YAML spec.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_savesession_gatewayfilter_factory" href="#_savesession_gatewayfilter_factory"></a>114.15&nbsp;SaveSession GatewayFilter Factory</h2></div></div></div><p>The SaveSession GatewayFilter Factory forces a <code class="literal">WebSession::save</code> operation <span class="emphasis"><em>before</em></span> forwarding the call downstream. This is of particular use when
using something like <a class="link" href="http://projects.spring.io/spring-session/" target="_top">Spring Session</a> with a lazy data store and need to ensure the session state has been saved before making the forwarded call.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: save_session
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/foo/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
- SaveSession</pre><p>
</p><p>If you are integrating <a class="link" href="http://projects.spring.io/spring-security/" target="_top">Spring Security</a> with Spring Session, and want to ensure security details have been forwarded to the remote process, this is critical.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_secureheaders_gatewayfilter_factory" href="#_secureheaders_gatewayfilter_factory"></a>114.16&nbsp;SecureHeaders GatewayFilter Factory</h2></div></div></div><p>The SecureHeaders GatewayFilter Factory adds a number of headers to the response at the reccomendation from <a class="link" href="https://blog.appcanary.com/2017/http-security-headers.html" target="_top">this blog post</a>.</p><div class="itemizedlist"><p class="title"><b>The following headers are added (allong with default values):</b></p><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">X-Xss-Protection:1; mode=block</code></li><li class="listitem"><code class="literal">Strict-Transport-Security:max-age=631138519</code></li><li class="listitem"><code class="literal">X-Frame-Options:DENY</code></li><li class="listitem"><code class="literal">X-Content-Type-Options:nosniff</code></li><li class="listitem"><code class="literal">Referrer-Policy:no-referrer</code></li><li class="listitem"><code class="literal">Content-Security-Policy:default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'</code></li><li class="listitem"><code class="literal">X-Download-Options:noopen</code></li><li class="listitem"><code class="literal">X-Permitted-Cross-Domain-Policies:none</code></li></ul></div><p>To change the default values set the appropriate property in the <code class="literal">spring.cloud.gateway.filter.secure-headers</code> namespace:</p><div class="itemizedlist"><p class="title"><b>Property to change:</b></p><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">xss-protection-header</code></li><li class="listitem"><code class="literal">strict-transport-security</code></li><li class="listitem"><code class="literal">frame-options</code></li><li class="listitem"><code class="literal">content-type-options</code></li><li class="listitem"><code class="literal">referrer-policy</code></li><li class="listitem"><code class="literal">content-security-policy</code></li><li class="listitem"><code class="literal">download-options</code></li><li class="listitem"><code class="literal">permitted-cross-domain-policies</code></li></ul></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_setpath_gatewayfilter_factory" href="#_setpath_gatewayfilter_factory"></a>114.17&nbsp;SetPath GatewayFilter Factory</h2></div></div></div><p>The SetPath GatewayFilter Factory takes a path <code class="literal">template</code> parameter. It offers a simple way to manipulate the request path by allowing templated segments of the path. This uses the uri templates from Spring Framework. Multiple matching segments are allowed.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: setpath_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/foo/{segment<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - SetPath</span>=/{segment<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre><p>
</p><p>For a request path of <code class="literal">/foo/bar</code>, this will set the path to <code class="literal">/bar</code> before making the downstream request.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_setresponseheader_gatewayfilter_factory" href="#_setresponseheader_gatewayfilter_factory"></a>114.18&nbsp;SetResponseHeader GatewayFilter Factory</h2></div></div></div><p>The SetResponseHeader GatewayFilter Factory takes <code class="literal">name</code> and <code class="literal">value</code> parameters.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: setresponseheader_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - SetResponseHeader</span>=X-Response-Foo<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span> Bar</pre><p>
</p><p>This GatewayFilter replaces all headers with the given name, rather than adding. So if the downstream server responded with a <code class="literal">X-Response-Foo:1234</code>, this would be replaced with <code class="literal">X-Response-Foo:Bar</code>, which is what the gateway client would receive.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_setstatus_gatewayfilter_factory" href="#_setstatus_gatewayfilter_factory"></a>114.19&nbsp;SetStatus GatewayFilter Factory</h2></div></div></div><p>The SetStatus GatewayFilter Factory takes a single <code class="literal">status</code> parameter. It must be a valid Spring <code class="literal">HttpStatus</code>. It may be the integer value <code class="literal">404</code> or the string representation of the enumeration <code class="literal">NOT_FOUND</code>.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: setstatusstring_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - SetStatus</span>=BAD_REQUEST
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: setstatusint_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - SetStatus</span>=<span class="hl-number">401</span></pre><p>
</p><p>In either case, the HTTP status of the response will be set to 401.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_stripprefix_gatewayfilter_factory" href="#_stripprefix_gatewayfilter_factory"></a>114.20&nbsp;StripPrefix GatewayFilter Factory</h2></div></div></div><p>The StripPrefix GatewayFilter Factory takes one paramter, <code class="literal">parts</code>. The <code class="literal">parts</code> parameter indicated the number of parts in the path to strip from the request before sending it downstream.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: nameRoot
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://nameservice
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/name/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - StripPrefix</span>=<span class="hl-number">2</span></pre><p>
</p><p>When a request is made through the gateway to <code class="literal">/name/bar/foo</code> the request made to <code class="literal">nameservice</code> will look like <code class="literal"><a class="link" href="http://nameservice/foo" target="_top">http://nameservice/foo</a></code>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_retry_gatewayfilter_factory" href="#_retry_gatewayfilter_factory"></a>114.21&nbsp;Retry GatewayFilter Factory</h2></div></div></div><p>The Retry GatewayFilter Factory takes <code class="literal">retries</code>, <code class="literal">statuses</code>, <code class="literal">methods</code>, and <code class="literal">series</code> as parameters.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">retries</code>: the number of retries that should be attempted</li><li class="listitem"><code class="literal">statuses</code>: the HTTP status codes that should be retried, represented using <code class="literal">org.springframework.http.HttpStatus</code></li><li class="listitem"><code class="literal">methods</code>: the HTTP methods that should be retried, represented using <code class="literal">org.springframework.http.HttpMethod</code></li><li class="listitem"><code class="literal">series</code>: the series of status codes to be retried, represented using <code class="literal">org.springframework.http.HttpStatus.Series</code></li></ul></div><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: retry_test
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://localhost:<span class="hl-number">8080</span>/flakey
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Host</span>=*.retry.com
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: Retry
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> retries</span>: <span class="hl-number">3</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> statuses</span>: BAD_GATEWAY</pre><p>
</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>When using the retry filter with a <code class="literal">forward:</code> prefixed URL, the target endpoint should be written carefully so that in case of an error it does not do anything that could result in a response being sent to the client and committed. For example, if the target endpoint is an annotated controller, the target controller method should not return <code class="literal">ResponseEntity</code> with an error status code. Instead it should throw an <code class="literal">Exception</code>, or signal an error, e.g. via a <code class="literal">Mono.error(ex)</code> return value, which the retry filter can be configured to handle by retrying.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_requestsize_gatewayfilter_factory" href="#_requestsize_gatewayfilter_factory"></a>114.22&nbsp;RequestSize GatewayFilter Factory</h2></div></div></div><p>The RequestSize GatewayFilter Factory can restrict a request from reaching the downstream service , when the request size is greater than the permissible limit. The filter takes <code class="literal">RequestSize</code> as parameter which is the permissible size limit of the request defined in bytes.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: request_size_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://localhost:<span class="hl-number">8080</span>/upload
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/upload
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: RequestSize
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> maxSize</span>: <span class="hl-number">5000000</span></pre><p>
</p><p>The RequestSize GatewayFilter Factory set the response status as <code class="literal">413 Payload Too Large</code> with a additional header <code class="literal">errorMessage</code> when the Request is rejected due to size. Following is an example of such an <code class="literal">errorMessage</code> .</p><p><code class="literal">errorMessage</code> : <code class="literal">Request size is larger than permissible limit. Request size is 6.0 MB where permissible limit is 5.0 MB</code></p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The default Request size will be set to 5 MB if not provided as filter argument in route definition.</p></td></tr></table></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_global_filters" href="#_global_filters"></a>115.&nbsp;Global Filters</h2></div></div></div><p>The <code class="literal">GlobalFilter</code> interface has the same signature as <code class="literal">GatewayFilter</code>. These are special filters that are conditionally applied to all routes. (This interface and usage are subject to change in future milestones).</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_combined_global_filter_and_gatewayfilter_ordering" href="#_combined_global_filter_and_gatewayfilter_ordering"></a>115.1&nbsp;Combined Global Filter and GatewayFilter Ordering</h2></div></div></div><p>When a request comes in (and matches a Route) the Filtering Web Handler will add all instances of <code class="literal">GlobalFilter</code> and all route specific instances of <code class="literal">GatewayFilter</code> to a filter chain. This combined filter chain is sorted by the <code class="literal">org.springframework.core.Ordered</code> interface, which can be set by implementing the <code class="literal">getOrder()</code> method or by using the <code class="literal">@Order</code> annotation.</p><p>As Spring Cloud Gateway distinguishes between "pre" and "post" phases for filter logic execution (see: How It Works), the filter with the highest precedence will be the first in the "pre"-phase and the last in the "post"-phase.</p><p><b>ExampleConfiguration.java.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@Order(-1)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GlobalFilter a() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; {
log.info(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"first pre filter"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange).then(Mono.fromRunnable(() -&gt; {
log.info(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"third post filter"</span>);
}));
};
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@Order(0)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GlobalFilter b() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; {
log.info(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"second pre filter"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange).then(Mono.fromRunnable(() -&gt; {
log.info(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"second post filter"</span>);
}));
};
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@Order(1)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GlobalFilter c() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; {
log.info(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"third pre filter"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange).then(Mono.fromRunnable(() -&gt; {
log.info(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"first post filter"</span>);
}));
};
}</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_forward_routing_filter" href="#_forward_routing_filter"></a>115.2&nbsp;Forward Routing Filter</h2></div></div></div><p>The <code class="literal">ForwardRoutingFilter</code> looks for a URI in the exchange attribute <code class="literal">ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR</code>. If the url has a <code class="literal">forward</code> scheme (ie <code class="literal">forward:///localendpoint</code>), it will use the Spring <code class="literal">DispatcherHandler</code> to handler the request. The path part of the request URL will be overridden with the path in the forward URL. The unmodified original url is appended to the list in the <code class="literal">ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR</code> attribute.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_loadbalancerclient_filter" href="#_loadbalancerclient_filter"></a>115.3&nbsp;LoadBalancerClient Filter</h2></div></div></div><p>The <code class="literal">LoadBalancerClientFilter</code> looks for a URI in the exchange attribute <code class="literal">ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR</code>. If the url has a <code class="literal">lb</code> scheme (ie <code class="literal">lb://myservice</code>), it will use the Spring Cloud <code class="literal">LoadBalancerClient</code> to resolve the name (<code class="literal">myservice</code> in the previous example) to an actual host and port and replace the URI in the same attribute. The unmodified original url is appended to the list in the <code class="literal">ServerWebExchangeUtils.GATEWAY_ORIGINAL_REQUEST_URL_ATTR</code> attribute. The filter will also look in the <code class="literal">ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR</code> attribute to see if it equals <code class="literal">lb</code> and then the same rules apply.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: myRoute
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: lb://service
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/service/**</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_netty_routing_filter" href="#_netty_routing_filter"></a>115.4&nbsp;Netty Routing Filter</h2></div></div></div><p>The Netty Routing Filter runs if the url located in the <code class="literal">ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR</code> exchange attribute has a <code class="literal">http</code> or <code class="literal">https</code> scheme. It uses the Netty <code class="literal">HttpClient</code> to make the downstream proxy request. The response is put in the <code class="literal">ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR</code> exchange attribute for use in a later filter. (There is an experimental <code class="literal">WebClientHttpRoutingFilter</code> that performs the same function, but does not require netty)</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_netty_write_response_filter" href="#_netty_write_response_filter"></a>115.5&nbsp;Netty Write Response Filter</h2></div></div></div><p>The <code class="literal">NettyWriteResponseFilter</code> runs if there is a Netty <code class="literal">HttpClientResponse</code> in the <code class="literal">ServerWebExchangeUtils.CLIENT_RESPONSE_ATTR</code> exchange attribute. It is run after all other filters have completed and writes the proxy response back to the gateway client response. (There is an experimental <code class="literal">WebClientWriteResponseFilter</code> that performs the same function, but does not require netty)</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_routetorequesturl_filter" href="#_routetorequesturl_filter"></a>115.6&nbsp;RouteToRequestUrl Filter</h2></div></div></div><p>The <code class="literal">RouteToRequestUrlFilter</code> runs if there is a <code class="literal">Route</code> object in the <code class="literal">ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR</code> exchange attribute. It creates a new URI, based off of the request URI, but updated with the URI attribute of the <code class="literal">Route</code> object. The new URI is placed in the <code class="literal">ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR</code> exchange attribute`.</p><p>If the URI has a scheme prefix, such as <code class="literal">lb:ws://serviceid</code>, the <code class="literal">lb</code> scheme is stripped from the URI and placed in the <code class="literal">ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR</code> for use later in the filter chain.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_websocket_routing_filter" href="#_websocket_routing_filter"></a>115.7&nbsp;Websocket Routing Filter</h2></div></div></div><p>The Websocket Routing Filter runs if the url located in the <code class="literal">ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR</code> exchange attribute has a <code class="literal">ws</code> or <code class="literal">wss</code> scheme. It uses the Spring Web Socket infrastructure to forward the Websocket request downstream.</p><p>Websockets may be load-balanced by prefixing the URI with <code class="literal">lb</code>, such as <code class="literal">lb:ws://serviceid</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you are using <a class="link" href="https://github.com/sockjs" target="_top">SockJS</a> as a fallback over normal http, you should configure a normal HTTP route as well as the Websocket Route.</p></td></tr></table></div><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># SockJS route</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: websocket_sockjs_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://localhost:<span class="hl-number">3001</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/websocket/info/**
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Normwal Websocket route</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: websocket_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: ws://localhost:<span class="hl-number">3001</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> predicates</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - Path</span>=/websocket/**</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_gateway_metrics_filter" href="#_gateway_metrics_filter"></a>115.8&nbsp;Gateway Metrics Filter</h2></div></div></div><p>To enable Gateway Metrics add spring-boot-starter-actuator as a project dependency. Then, by default, the Gateway Metrics Filter runs as long as the property <code class="literal">spring.cloud.gateway.metrics.enabled</code> is not set to <code class="literal">false</code>. This filter adds a timer metric named "gateway.requests" with the following tags:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">routeId</code>: The route id</li><li class="listitem"><code class="literal">routeUri</code>: The URI that the API will be routed to</li><li class="listitem"><code class="literal">outcome</code>: Outcome as classified by <a class="link" href="https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/HttpStatus.Series.html" target="_top">HttpStatus.Series</a></li><li class="listitem"><code class="literal">status</code>: Http Status of the request returned to the client</li></ul></div><p>These metrics are then available to be scraped from <code class="literal">/actuator/metrics/gateway.requests</code> and can be easily integated with Prometheus to create a <a class="link" href="images/gateway-grafana-dashboard.jpeg" target="_top">Grafana</a> <a class="link" href="gateway-grafana-dashboard.json" target="_top">dashboard</a>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>To enable the pometheus endpoint add micrometer-registry-prometheus as a project dependency.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_making_an_exchange_as_routed" href="#_making_an_exchange_as_routed"></a>115.9&nbsp;Making An Exchange As Routed</h2></div></div></div><p>After the Gateway has routed a <code class="literal">ServerWebExchange</code> it will mark that exchange as "routed" by adding <code class="literal">gatewayAlreadyRouted</code>
to the exchange attributes. Once a request has been marked as routed, other routing filters will not route the request again,
essentially skipping the filter. There are convenience methods that you can use to mark an exchange as routed
or check if an exchange has already been routed.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">ServerWebExchangeUtils.isAlreadyRouted</code> takes a <code class="literal">ServerWebExchange</code> object and checks if it has been "routed"</li><li class="listitem"><code class="literal">ServerWebExchangeUtils.setAlreadyRouted</code> takes a <code class="literal">ServerWebExchange</code> object and marks it as "routed"</li></ul></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_tls_ssl" href="#_tls_ssl"></a>116.&nbsp;TLS / SSL</h2></div></div></div><p>The Gateway can listen for requests on https by following the usual Spring server configuration. Example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ssl</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key-alias</span>: scg
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key-store-password</span>: scg1234
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key-store</span>: classpath:scg-keystore.p12
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key-store-type</span>: PKCS12</pre><p>
</p><p>Gateway routes can be routed to both http and https backends. If routing to a https backend then the Gateway can be configured to trust all downstream certificates with the following configuration:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> httpclient</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ssl</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> useInsecureTrustManager</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre><p>
</p><p>Using an insecure trust manager is not suitable for production. For a production deployment the Gateway can be configured with a set of known certificates that it can trust with the follwing configuration:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> httpclient</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ssl</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> trustedX509Certificates</span>:
- cert1.pem
- cert2.pem</pre><p>
</p><p>If the Spring Cloud Gateway is not provisioned with trusted certificates the default trust store is used (which can be overriden with system property javax.net.ssl.trustStore).</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_tls_handshake" href="#_tls_handshake"></a>116.1&nbsp;TLS Handshake</h2></div></div></div><p>The Gateway maintains a client pool that it uses to route to backends. When communicating over https the client initiates a TLS handshake. A number of timeouts are assoicated with this handshake. These timeouts can be configured (defaults shown):</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> httpclient</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> ssl</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> handshake-timeout-millis</span>: <span class="hl-number">10000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> close-notify-flush-timeout-millis</span>: <span class="hl-number">3000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> close-notify-read-timeout-millis</span>: <span class="hl-number">0</span></pre><p>
</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_configuration_2" href="#_configuration_2"></a>117.&nbsp;Configuration</h2></div></div></div><p>Configuration for Spring Cloud Gateway is driven by a collection of `RouteDefinitionLocator`s.</p><p><b>RouteDefinitionLocator.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> RouteDefinitionLocator {
Flux&lt;RouteDefinition&gt; getRouteDefinitions();
}</pre><p>
</p><p>By default, a <code class="literal">PropertiesRouteDefinitionLocator</code> loads properties using Spring Boot&#8217;s <code class="literal">@ConfigurationProperties</code> mechanism.</p><p>The configuration examples above all use a shortcut notation that uses positional arguments rather than named ones. The two examples below are equivalent:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: setstatus_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: SetStatus
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> status</span>: <span class="hl-number">401</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: setstatusshortcut_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: http://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - SetStatus</span>=<span class="hl-number">401</span></pre><p>
</p><p>For some usages of the gateway, properties will be adequate, but some production use cases will benefit from loading configuration from an external source, such as a database. Future milestone versions will have <code class="literal">RouteDefinitionLocator</code> implementations based off of Spring Data Repositories such as: Redis, MongoDB and Cassandra.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_fluent_java_routes_api" href="#_fluent_java_routes_api"></a>117.1&nbsp;Fluent Java Routes API</h2></div></div></div><p>To allow for simple configuration in Java, there is a fluent API defined in the <code class="literal">RouteLocatorBuilder</code> bean.</p><p><b>GatewaySampleApplication.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// static imports from GatewayFilters and RoutePredicates</span>
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> builder.routes()
.route(r -&gt; r.host(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"**.abc.org"</span>).and().path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/png"</span>)
.filters(f -&gt;
f.addResponseHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"X-TestHeader"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foobar"</span>))
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://httpbin.org:80"</span>)
)
.route(r -&gt; r.path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/webp"</span>)
.filters(f -&gt;
f.addResponseHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"X-AnotherHeader"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"baz"</span>))
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://httpbin.org:80"</span>)
)
.route(r -&gt; r.order(-<span class="hl-number">1</span>)
.host(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"**.throttle.org"</span>).and().path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/get"</span>)
.filters(f -&gt; f.filter(throttle.apply(<span class="hl-number">1</span>,
<span class="hl-number">1</span>,
<span class="hl-number">10</span>,
TimeUnit.SECONDS)))
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://httpbin.org:80"</span>)
)
.build();
}</pre><p>
</p><p>This style also allows for more custom predicate assertions. The predicates defined by <code class="literal">RouteDefinitionLocator</code> beans are combined using logical <code class="literal">and</code>. By using the fluent Java API, you can use the <code class="literal">and()</code>, <code class="literal">or()</code> and <code class="literal">negate()</code> operators on the <code class="literal">Predicate</code> class.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_discoveryclient_route_definition_locator" href="#_discoveryclient_route_definition_locator"></a>117.2&nbsp;DiscoveryClient Route Definition Locator</h2></div></div></div><p>The Gateway can be configured to create routes based on services registered with a <code class="literal">DiscoveryClient</code> compatible service registry.</p><p>To enable this, set <code class="literal">spring.cloud.gateway.discovery.locator.enabled=true</code> and make sure a <code class="literal">DiscoveryClient</code> implementation is on the classpath and enabled (such as Netflix Eureka, Consul or Zookeeper).</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_reactor_netty_access_logs" href="#_reactor_netty_access_logs"></a>118.&nbsp;Reactor Netty Access Logs</h2></div></div></div><p>To enable Reactor Netty access logs, set <code class="literal">-Dreactor.netty.http.server.accessLogEnabled=true</code>. (It must be a Java System Property, not a Spring Boot property).</p><p>The logging system can be configured to have a separate access log file. Below is an example logback configuration:</p><p><b>logback.xml.&nbsp;</b>
</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appender</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"accessLog"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.core.FileAppender"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;file&gt;</span>access_log.log<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/file&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;encoder&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;pattern&gt;</span>%msg%n<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/pattern&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/encoder&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/appender&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appender</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"async"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">class</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"ch.qos.logback.classic.AsyncAppender"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appender-ref</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">ref</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"accessLog"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag"> /&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/appender&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;logger</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">name</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"reactor.netty.http.server.AccessLog"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">level</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"INFO"</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">additivity</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"false"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;appender-ref</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">ref</span>=<span xmlns:d="http://docbook.org/ns/docbook" class="hl-value">"async"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">/&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/logger&gt;</span></pre><p>
</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_cors_configuration" href="#_cors_configuration"></a>119.&nbsp;CORS Configuration</h2></div></div></div><p>The gateway can be configured to control CORS behavior. The "global" CORS configuration is a map of URL patterns to <a class="link" href="https://docs.spring.io/spring/docs/5.0.x/javadoc-api/org/springframework/web/cors/CorsConfiguration.html" target="_top">Spring Framework <code class="literal">CorsConfiguration</code></a>.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> globalcors</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> corsConfigurations</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> '[/**]'</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> allowedOrigins</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"docs.spring.io"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> allowedMethods</span>:
- GET</pre><p>
</p><p>In the example above, CORS requests will be allowed from requests that originate from docs.spring.io for all GET requested paths.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_actuator_api" href="#_actuator_api"></a>120.&nbsp;Actuator API</h2></div></div></div><p>TODO: document the <code class="literal">/gateway</code> actuator endpoint</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_developer_guide" href="#_developer_guide"></a>121.&nbsp;Developer Guide</h2></div></div></div><p>TODO: overview of writing custom integrations</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_route_predicate_factories" href="#_writing_custom_route_predicate_factories"></a>121.1&nbsp;Writing Custom Route Predicate Factories</h2></div></div></div><p>TODO: document writing Custom Route Predicate Factories</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_gatewayfilter_factories" href="#_writing_custom_gatewayfilter_factories"></a>121.2&nbsp;Writing Custom GatewayFilter Factories</h2></div></div></div><p>In order to write a GatewayFilter you will need to implement <code class="literal">GatewayFilterFactory</code>. There is an abstract class called <code class="literal">AbstractGatewayFilterFactory</code> which you can extend.</p><p><b>PreGatewayFilterFactory.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PreGatewayFilterFactory <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AbstractGatewayFilterFactory&lt;PreGatewayFilterFactory.Config&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> PreGatewayFilterFactory() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>(Config.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GatewayFilter apply(Config config) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// grab configuration from Config object</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//If you want to build a "pre" filter you need to manipulate the</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//request before calling change.filter</span>
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//use builder to manipulate the request</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange.mutate().request(request).build());
};
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Put the configuration properties for your filter here</span>
}
}</pre><p>
</p><p><b>PostGatewayFilterFactory.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PostGatewayFilterFactory <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AbstractGatewayFilterFactory&lt;PostGatewayFilterFactory.Config&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> PostGatewayFilterFactory() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>(Config.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GatewayFilter apply(Config config) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// grab configuration from Config object</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange).then(Mono.fromRunnable(() -&gt; {
ServerHttpResponse response = exchange.getResponse();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Manipulate the response in some way</span>
}));
};
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Put the configuration properties for your filter here</span>
}
}</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_global_filters" href="#_writing_custom_global_filters"></a>121.3&nbsp;Writing Custom Global Filters</h2></div></div></div><p>TODO: document writing Custom Global Filters</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_route_locators_and_writers" href="#_writing_custom_route_locators_and_writers"></a>121.4&nbsp;Writing Custom Route Locators and Writers</h2></div></div></div><p>TODO: document writing Custom Route Locators and Writers</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_building_a_simple_gateway_using_spring_mvc_or_webflux" href="#_building_a_simple_gateway_using_spring_mvc_or_webflux"></a>122.&nbsp;Building a Simple Gateway Using Spring MVC or Webflux</h2></div></div></div><p>Spring Cloud Gateway provides a utility object called <code class="literal">ProxyExchange</code> which you can use inside a regular Spring web handler as a method parameter. It supports basic downstream HTTP exchanges via methods that mirror the HTTP verbs. With MVC it also supports forwarding to a local handler via the <code class="literal">forward()</code> method. To use the <code class="literal">ProxyExchange</code> just include the right module in your classpath (either <code class="literal">spring-cloud-gateway-mvc</code> or <code class="literal">spring-cloud-gateway-webflux</code>).</p><p>MVC example (proxying a request to "/test" downstream to a remote server):</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> GatewaySampleApplication {
<em><span class="hl-annotation" style="color: gray">@Value("${remote.home}")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> URI home;
<em><span class="hl-annotation" style="color: gray">@GetMapping("/test")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ResponseEntity&lt;?&gt; proxy(ProxyExchange&lt;<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]&gt; proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/png"</span>).get();
}
}</pre><p>The same thing with Webflux:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> GatewaySampleApplication {
<em><span class="hl-annotation" style="color: gray">@Value("${remote.home}")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> URI home;
<em><span class="hl-annotation" style="color: gray">@GetMapping("/test")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Mono&lt;ResponseEntity&lt;?&gt;&gt; proxy(ProxyExchange&lt;<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]&gt; proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/png"</span>).get();
}
}</pre><p>There are convenience methods on the <code class="literal">ProxyExchange</code> to enable the handler method to discover and enhance the URI path of the incoming request. For example you might want to extract the trailing elements of a path to pass them downstream:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@GetMapping("/proxy/path/**")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ResponseEntity&lt;?&gt; proxyPath(ProxyExchange&lt;<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]&gt; proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
String path = proxy.path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/proxy/path/"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foos/"</span> + path).get();
}</pre><p>All the features of Spring MVC or Webflux are available to Gateway handler methods. So you can inject request headers and query parameters, for instance, and you can constrain the incoming requests with declarations in the mapping annotation. See the documentation for <code class="literal">@RequestMapping</code> in Spring MVC for more details of those features.</p><p>Headers can be added to the downstream response using the <code class="literal">header()</code> methods on <code class="literal">ProxyExchange</code>.</p><p>You can also manipulate response headers (and anything else you like in the response) by adding a mapper to the <code class="literal">get()</code> etc. method. The mapper is a <code class="literal">Function</code> that takes the incoming <code class="literal">ResponseEntity</code> and converts it to an outgoing one.</p><p>First class support is provided for "sensitive" headers ("cookie" and "authorization" by default) which are not passed downstream, and for "proxy" headers (<code class="literal">x-forwarded-*</code>).</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_function_2" href="#_spring_cloud_function_2"></a>Part&nbsp;XVI.&nbsp;Spring Cloud Function</h1></div></div></div><div class="partintro"><div></div><p>Mark Fisher, Dave Syer, Oleg Zhurakousky</p><p></p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_introduction_2" href="#_introduction_2"></a>123.&nbsp;Introduction</h2></div></div></div><p>Spring Cloud Function is a project with the following high-level goals:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Promote the implementation of business logic via functions.</li><li class="listitem">Decouple the development lifecycle of business logic from any specific runtime target so that the same code can run as a web endpoint, a stream processor, or a task.</li><li class="listitem">Support a uniform programming model across serverless providers, as well as the ability to run standalone (locally or in a PaaS).</li><li class="listitem">Enable Spring Boot features (auto-configuration, dependency injection, metrics) on serverless providers.</li></ul></div><p>It abstracts away all of the transport details and
infrastructure, allowing the developer to keep all the familiar tools
and processes, and focus firmly on business logic.</p><p>Here&#8217;s a complete, executable, testable Spring Boot application
(implementing a simple string manipulation):</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Function&lt;Flux&lt;String&gt;, Flux&lt;String&gt;&gt; uppercase() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> flux -&gt; flux.map(value -&gt; value.toUpperCase());
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre><p>It&#8217;s just a Spring Boot application, so it can be built, run and
tested, locally and in a CI build, the same way as any other Spring
Boot application. The <code class="literal">Function</code> is from <code class="literal">java.util</code> and <code class="literal">Flux</code> is a
<a class="link" href="http://www.reactive-streams.org/" target="_top">Reactive Streams</a> <code class="literal">Publisher</code> from
<a class="link" href="https://projectreactor.io/" target="_top">Project Reactor</a>. The function can be
accessed over HTTP or messaging.</p><p>Spring Cloud Function has 4 main features:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem">Wrappers for <code class="literal">@Beans</code> of type <code class="literal">Function</code>, <code class="literal">Consumer</code> and
<code class="literal">Supplier</code>, exposing them to the outside world as either HTTP
endpoints and/or message stream listeners/publishers with RabbitMQ, Kafka etc.</li><li class="listitem">Compiling strings which are Java function bodies into bytecode, and
then turning them into <code class="literal">@Beans</code> that can be wrapped as above.</li><li class="listitem">Deploying a JAR file containing such an application context with an
isolated classloader, so that you can pack them together in a single
JVM.</li><li class="listitem">Adapters for <a class="link" href="https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws" target="_top">AWS Lambda</a>, <a class="link" href="https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure" target="_top">Azure</a>, <a class="link" href="https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk" target="_top">Apache OpenWhisk</a> and possibly other "serverless" service providers.</li></ol></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Spring Cloud is released under the non-restrictive Apache 2.0 license. If you would like to contribute to this section of the documentation or if you find an error, please find the source code and issue trackers in the project at <a class="link" href="https://github.com/spring-cloud/spring-cloud-function/tree/master/docs/src/main/asciidoc" target="_top">github</a>.</p></td></tr></table></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_getting_started" href="#_getting_started"></a>124.&nbsp;Getting Started</h2></div></div></div><p>Build from the command line (and "install" the samples):</p><pre class="screen">$ ./mvnw clean install</pre><p>(If you like to YOLO add <code class="literal">-DskipTests</code>.)</p><p>Run one of the samples, e.g.</p><pre class="screen">$ java -jar spring-cloud-function-samples/function-sample/target/*.jar</pre><p>This runs the app and exposes its functions over HTTP, so you can
convert a string to uppercase, like this:</p><pre class="screen">$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d Hello
HELLO</pre><p>You can convert multiple strings (a <code class="literal">Flux&lt;String&gt;</code>) by separating them
with new lines</p><pre class="screen">$ curl -H "Content-Type: text/plain" localhost:8080/uppercase -d 'Hello
&gt; World'
HELLOWORLD</pre><p>(You can use <code class="literal"><sup>Q</sup>J</code> in a terminal to insert a new line in a literal
string like that.)</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_building_and_running_a_function" href="#_building_and_running_a_function"></a>125.&nbsp;Building and Running a Function</h2></div></div></div><p>The sample <code class="literal">@SpringBootApplication</code> above has a function that can be
decorated at runtime by Spring Cloud Function to be an HTTP endpoint,
or a Stream processor, for instance with RabbitMQ, Apache Kafka or
JMS.</p><p>The <code class="literal">@Beans</code> can be <code class="literal">Function</code>, <code class="literal">Consumer</code> or <code class="literal">Supplier</code> (all from
<code class="literal">java.util</code>), and their parametric types can be String or POJO.</p><p>Functions can also be of <code class="literal">Flux&lt;String&gt;</code> or <code class="literal">Flux&lt;Pojo&gt;</code> and Spring
Cloud Function takes care of converting the data to and from the
desired types, as long as it comes in as plain text or (in the case of
the POJO) JSON. There is also support for <code class="literal">Message&lt;Pojo&gt;</code> where the
message headers are copied from the incoming event, depending on the
adapter. The web adapter also supports conversion from form-encoded
data to a <code class="literal">Map</code>, and if you are using the function with Spring Cloud
Stream then all the conversion and coercion features for message
payloads will be applicable as well.</p><p>Functions can be grouped together in a single application, or deployed
one-per-jar. It&#8217;s up to the developer to choose. An app with multiple
functions can be deployed multiple times in different "personalities",
exposing different functions over different physical transports.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_function_catalog_and_flexible_function_signatures" href="#_function_catalog_and_flexible_function_signatures"></a>126.&nbsp;Function Catalog and Flexible Function Signatures</h2></div></div></div><p>One of the main features of Spring Cloud Function is to adapt and support a range of type signatures for user-defined functions,
while providing a consistent execution model.
That&#8217;s why all user defined functions are transformed into a canonical representation by <code class="literal">FunctionCatalog</code>, using primitives
defined by the <a class="link" href="https://projectreactor.io/" target="_top">Project Reactor</a> (i.e., <code class="literal">Flux&lt;T&gt;</code> and <code class="literal">Mono&lt;T&gt;</code>).
Users can supply a bean of type <code class="literal">Function&lt;String,String&gt;</code>, for instance, and the <code class="literal">FunctionCatalog</code> will wrap it into a
<code class="literal">Function&lt;Flux&lt;String&gt;,Flux&lt;String&gt;&gt;</code>.</p><p>Using Reactor based primitives not only helps with the canonical representation of user defined functions, but it also
facilitates a more robust and flexible(reactive) execution model.</p><p>While users don&#8217;t normally have to care about the <code class="literal">FunctionCatalog</code> at all, it is useful to know what
kind of functions are supported in user code.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_java_8_function_support" href="#_java_8_function_support"></a>126.1&nbsp;Java 8 function support</h2></div></div></div><p>Generally speaking users can expect that if they write a function for
a plain old Java type (or primitive wrapper), then the function
catalog will wrap it to a <code class="literal">Flux</code> of the same type. If the user writes
a function using <code class="literal">Message</code> (from spring-messaging) it will receive and
transmit headers from any adapter that supports key-value metadata
(e.g. HTTP headers). Here are the details.</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">User Function</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Catalog Registration</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Function&lt;S,T&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;S&gt;, Flux&lt;T&gt;&gt;</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Message&lt;S&gt;,Message&lt;T&gt;&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;Message&lt;S&gt;&gt;, Flux&lt;Message&lt;T&gt;&gt;&gt;</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;S&gt;, Flux&lt;T&gt;&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;S&gt;, Flux&lt;T&gt;&gt;</code> (pass through)</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Supplier&lt;T&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Supplier&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Supplier&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Supplier&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Consumer&lt;T&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;T&gt;, Mono&lt;Void&gt;&gt;</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Consumer&lt;Message&lt;T&gt;&gt;</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;Message&lt;T&gt;&gt;, Mono&lt;Void&gt;&gt;</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Consumer&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">Consumer&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="" align="left" valign="top">&nbsp;</td></tr></tbody></table></div><p>Consumer is a little bit special because it has a <code class="literal">void</code> return type,
which implies blocking, at least potentially. Most likely you will not
need to write <code class="literal">Consumer&lt;Flux&lt;?&gt;&gt;</code>, but if you do need to do that,
remember to subscribe to the input flux. If you declare a <code class="literal">Consumer</code>
of a non publisher type (which is normal), it will be converted to a
function that returns a publisher, so that it can be subscribed to in
a controlled way.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_kotlin_lambda_support" href="#_kotlin_lambda_support"></a>126.2&nbsp;Kotlin Lambda support</h2></div></div></div><p>We also provide support for Kotlin lambdas (since v2.0).
Consider the following:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
open fun kotlinSupplier(): () -&gt; String {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello from Kotlin"</span> }
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
open fun kotlinFunction(): (String) -&gt; String {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> { it.toUpperCase() }
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
open fun kotlinConsumer(): (String) -&gt; Unit {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> { println(it) }
}</pre><p>The above represents Kotlin lambdas configured as Spring beans. The signature of each maps to a Java equivalent of
<code class="literal">Supplier</code>, <code class="literal">Function</code> and <code class="literal">Consumer</code>, and thus supported/recognized signatures by the framework.
While mechanics of Kotlin-to-Java mapping are outside of the scope of this documentation, it is important to understand that the
same rules for signature transformation outlined in "Java 8 function support" section are applied here as well.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_standalone_web_applications" href="#_standalone_web_applications"></a>127.&nbsp;Standalone Web Applications</h2></div></div></div><p>The <code class="literal">spring-cloud-function-web</code> module has autoconfiguration that
activates when it is included in a Spring Boot web application (with
MVC support). There is also a <code class="literal">spring-cloud-starter-function-web</code> to
collect all the optional dependencies in case you just want a simple
getting started experience.</p><p>With the web configurations activated your app will have an MVC
endpoint (on "/" by default, but configurable with
<code class="literal">spring.cloud.function.web.path</code>) that can be used to access the
functions in the application context. The supported content types are
plain text and JSON.</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"><col class="col_4"><col class="col_5"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Method</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Path</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Request</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Response</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Status</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>GET</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/{supplier}</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>-</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Items from the named supplier</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>200 OK</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>POST</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/{consumer}</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>JSON object or text</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mirrors input and pushes request body into consumer</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>202 Accepted</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>POST</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/{consumer}</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>JSON array or text with new lines</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mirrors input and pushes body into consumer one by one</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>202 Accepted</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>POST</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/{function}</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>JSON object or text</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The result of applying the named function</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>200 OK</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>POST</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/{function}</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>JSON array or text with new lines</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The result of applying the named function</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>200 OK</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>GET</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>/{function}/{item}</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>-</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Convert the item into an object and return the result of applying the function</p></td><td style="" align="left" valign="top"><p>200 OK</p></td></tr></tbody></table></div><p>As the table above shows the behaviour of the endpoint depends on the method and also the type of incoming request data. When the incoming data is single valued, and the target function is declared as obviously single valued (i.e. not returning a collection or <code class="literal">Flux</code>), then the response will also contain a single value. For multi-valued responses the client can ask for a server-sent event stream by sending `Accept: text/event-stream". If there is only one function (consumer etc.) then the name in the path is optional. Composite functions can be addressed using pipes or commas to separate function names (pipes are legal in URL paths, but a bit awkward to type on the command line).</p><p>Functions and consumers that are declared with input and output in <code class="literal">Message&lt;?&gt;</code> will see the request headers on the input messages, and the output message headers will be converted to HTTP headers.</p><p>When POSTing text the response format might be different with Spring Boot 2.0 and older versions, depending on the content negotiation (provide content type and accpt headers for the best results).</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_standalone_streaming_applications" href="#_standalone_streaming_applications"></a>128.&nbsp;Standalone Streaming Applications</h2></div></div></div><p>To send or receive messages from a broker (such as RabbitMQ or Kafka) you can leverage <code class="literal">spring-cloud-stream</code> project and it&#8217;s integration with Spring Cloud Function.
Please refer to <a class="link" href="https://docs.spring.io/spring-cloud-stream/docs/current/reference/htmlsingle/#_spring_cloud_function" target="_top">Spring Cloud Function</a> section of the Spring Cloud Stream reference manual for more details and examples.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_deploying_a_packaged_function" href="#_deploying_a_packaged_function"></a>129.&nbsp;Deploying a Packaged Function</h2></div></div></div><p>Spring Cloud Function provides a "deployer" library that allows you to launch a jar file (or exploded archive, or set of jar files) with an isolated class loader and expose the functions defined in it. This is quite a powerful tool that would allow you to, for instance, adapt a function to a range of different input-output adapters without changing the target jar file. Serverless platforms often have this kind of feature built in, so you could see it as a building block for a function invoker in such a platform (indeed the <a class="link" href="https://projectriff.io" target="_top">Riff</a> Java function invoker uses this library).</p><p>The standard entry point of the API is the Spring configuration annotation <code class="literal">@EnableFunctionDeployer</code>. If that is used in a Spring Boot application the deployer kicks in and looks for some configuration to tell it where to find the function jar. At a minimum the user has to provide a <code class="literal">function.location</code> which is a URL or resource location for the archive containing the functions. It can optionally use a <code class="literal">maven:</code> prefix to locate the artifact via a dependency lookup (see <code class="literal">FunctionProperties</code> for complete details). A Spring Boot application is bootstrapped from the jar file, using the <code class="literal">MANIFEST.MF</code> to locate a start class, so that a standard Spring Boot fat jar works well, for example. If the target jar can be launched successfully then the result is a function registered in the main application&#8217;s <code class="literal">FunctionCatalog</code>. The registered function can be applied by code in the main application, even though it was created in an isolated class loader (by deault).</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_functional_bean_definitions" href="#_functional_bean_definitions"></a>130.&nbsp;Functional Bean Definitions</h2></div></div></div><p>Spring Cloud Function supports a "functional" style of bean declarations for small apps where you need fast startup. The functional style of bean declaration was a feature of Spring Framework 5.0 with significant enhancements in 5.1.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_comparing_functional_with_traditional_bean_definitions" href="#_comparing_functional_with_traditional_bean_definitions"></a>130.1&nbsp;Comparing Functional with Traditional Bean Definitions</h2></div></div></div><p>Here&#8217;s a vanilla Spring Cloud Function application from with the
familiar <code class="literal">@Configuration</code> and <code class="literal">@Bean</code> declaration style:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> DemoApplication {
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Function&lt;String, String&gt; uppercase() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> value -&gt; value.toUpperCase();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(DemoApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre><p>You can run the above in a serverless platform, like AWS Lambda or Azure Functions, or you can run it in its own HTTP server just by including <code class="literal">spring-cloud-function-starter-web</code> on the classpath. Running the main method would expose an endpoint that you can use to ping that <code class="literal">uppercase</code> function:</p><pre class="screen">$ curl localhost:8080 -d foo
FOO</pre><p>The web adapter in <code class="literal">spring-cloud-function-starter-web</code> uses Spring MVC, so you needed a Servlet container. You can also use Webflux where the default server is netty (even though you can still use Servlet containers if you want to) - just include the <code class="literal">spring-cloud-starter-function-webflux</code> dependency instead. The functionality is the same, and the user application code can be used in both.</p><p>Now for the functional beans: the user application code can be recast into "functional"
form, like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> DemoApplication <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> ApplicationContextInitializer&lt;GenericApplicationContext&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
FunctionalSpringApplication.run(DemoApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Function&lt;String, String&gt; uppercase() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> value -&gt; value.toUpperCase();
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> initialize(GenericApplicationContext context) {
context.registerBean(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"demo"</span>, FunctionRegistration.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>,
() -&gt; <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> FunctionRegistration&lt;&gt;(uppercase())
.type(FunctionType.from(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).to(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)));
}
}</pre><p>The main differences are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The main class is an <code class="literal">ApplicationContextInitializer</code>.</li><li class="listitem">The <code class="literal">@Bean</code> methods have been converted to calls to <code class="literal">context.registerBean()</code></li><li class="listitem">The <code class="literal">@SpringBootApplication</code> has been replaced with
<code class="literal">@SpringBootConfiguration</code> to signify that we are not enabling Spring
Boot autoconfiguration, and yet still marking the class as an "entry
point".</li><li class="listitem">The <code class="literal">SpringApplication</code> from Spring Boot has been replaced with a
<code class="literal">FunctionalSpringApplication</code> from Spring Cloud Function (it&#8217;s a
subclass).</li></ul></div><p>The business logic beans that you register in a Spring Cloud Function app are of type <code class="literal">FunctionRegistration</code>. This is a wrapper that contains both the function and information about the input and output types. In the <code class="literal">@Bean</code> form of the application that information can be derived reflectively, but in a functional bean registration some of it is lost unless we use a <code class="literal">FunctionRegistration</code>.</p><p>An alternative to using an <code class="literal">ApplicationContextInitializer</code> and <code class="literal">FunctionRegistration</code> is to make the application itself implement <code class="literal">Function</code> (or <code class="literal">Consumer</code> or <code class="literal">Supplier</code>). Example (equivalent to the above):</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootConfiguration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> DemoApplication <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> Function&lt;String, String&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
FunctionalSpringApplication.run(DemoApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String uppercase(String value) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> value.toUpperCase();
}
}</pre><p>It would also work if you add a separate, standalone class of type <code class="literal">Function</code> and register it with the <code class="literal">SpringApplication</code> using an alternative form of the <code class="literal">run()</code> method. The main thing is that the generic type information is available at runtime through the class declaration.</p><p>The app runs in its own HTTP server if you add <code class="literal">spring-cloud-starter-function-webflux</code> (it won&#8217;t work with the MVC starter at the moment because the functional form of the embedded Servlet container hasn&#8217;t been implemented). The app also runs just fine in AWS Lambda or Azure Functions, and the improvements in startup time are dramatic.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The "lite" web server has some limitations for the range of <code class="literal">Function</code> signatures - in particular it doesn&#8217;t (yet) support <code class="literal">Message</code> input and output, but POJOs and any kind of <code class="literal">Publisher</code> should be fine.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_testing_functional_applications" href="#_testing_functional_applications"></a>130.2&nbsp;Testing Functional Applications</h2></div></div></div><p>Spring Cloud Function also has some utilities for integration testing that will be very familiar to Spring Boot users. For example, here is an integration test for the HTTP server wrapping the app above:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@FunctionalSpringBootTest</span></em>
<em><span class="hl-annotation" style="color: gray">@AutoConfigureWebTestClient</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FunctionalTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> WebTestClient client;
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> words() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
client.post().uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/"</span>).body(Mono.just(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>), String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).exchange()
.expectStatus().isOk().expectBody(String.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FOO"</span>);
}
}</pre><p>This test is almost identical to the one you would write for the <code class="literal">@Bean</code> version of the same app - the only difference is the <code class="literal">@FunctionalSpringBootTest</code> annotation, instead of the regular <code class="literal">@SpringBootTest</code>. All the other pieces, like the <code class="literal">@Autowired</code> <code class="literal">WebTestClient</code>, are standard Spring Boot features.</p><p>Or you could write a test for a non-HTTP app using just the <code class="literal">FunctionCatalog</code>. For example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RunWith(SpringRunner.class)</span></em>
<em><span class="hl-annotation" style="color: gray">@FunctionalSpringBootTest</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FunctionalTests {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> FunctionCatalog catalog;
<em><span class="hl-annotation" style="color: gray">@Test</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> words() <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
Function&lt;Flux&lt;String&gt;, Flux&lt;String&gt;&gt; function = catalog.lookup(Function.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"function"</span>);
assertThat(function.apply(Flux.just(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>)).blockFirst()).isEqualTo(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"FOO"</span>);
}
}</pre><p>(The <code class="literal">FunctionCatalog</code> always returns functions from <code class="literal">Flux</code> to <code class="literal">Flux</code>, even if the user declares them with a simpler signature.)</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_limitations_of_functional_bean_declaration" href="#_limitations_of_functional_bean_declaration"></a>130.3&nbsp;Limitations of Functional Bean Declaration</h2></div></div></div><p>Most Spring Cloud Function apps have a relatively small scope compared to the whole of Spring Boot, so we are able to adapt it to these functional bean definitions easily. If you step outside that limited scope, you can extend your Spring Cloud Function app by switching back to <code class="literal">@Bean</code> style configuration, or by using a hybrid approach. If you want to take advantage of Spring Boot autoconfiguration for integrations with external datastores, for example, you will need to use <code class="literal">@EnableAutoConfiguration</code>. Your functions can still be defined using the functional declarations if you want (i.e. the "hybrid" style), but in that case you will need to explicitly switch off the "full functional mode" using <code class="literal">spring.functional.enabled=false</code> so that Spring Boot can take back control.</p></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_dynamic_compilation" href="#_dynamic_compilation"></a>131.&nbsp;Dynamic Compilation</h2></div></div></div><p>There is a sample app that uses the function compiler to create a
function from a configuration property. The vanilla "function-sample"
also has that feature. And there are some scripts that you can run to
see the compilation happening at run time. To run these examples,
change into the <code class="literal">scripts</code> directory:</p><pre class="screen">cd scripts</pre><p>Also, start a RabbitMQ server locally (e.g. execute <code class="literal">rabbitmq-server</code>).</p><p>Start the Function Registry Service:</p><pre class="screen">./function-registry.sh</pre><p>Register a Function:</p><pre class="screen">./registerFunction.sh -n uppercase -f "f-&gt;f.map(s-&gt;s.toString().toUpperCase())"</pre><p>Run a REST Microservice using that Function:</p><pre class="screen">./web.sh -f uppercase -p 9000
curl -H "Content-Type: text/plain" -H "Accept: text/plain" localhost:9000/uppercase -d foo</pre><p>Register a Supplier:</p><pre class="screen">./registerSupplier.sh -n words -f "()-&gt;Flux.just(\"foo\",\"bar\")"</pre><p>Run a REST Microservice using that Supplier:</p><pre class="screen">./web.sh -s words -p 9001
curl -H "Accept: application/json" localhost:9001/words</pre><p>Register a Consumer:</p><pre class="screen">./registerConsumer.sh -n print -t String -f "System.out::println"</pre><p>Run a REST Microservice using that Consumer:</p><pre class="screen">./web.sh -c print -p 9002
curl -X POST -H "Content-Type: text/plain" -d foo localhost:9002/print</pre><p>Run Stream Processing Microservices:</p><p>First register a streaming words supplier:</p><pre class="screen">./registerSupplier.sh -n wordstream -f "()-&gt;Flux.interval(Duration.ofMillis(1000)).map(i-&gt;\"message-\"+i)"</pre><p>Then start the source (supplier), processor (function), and sink (consumer) apps
(in reverse order):</p><pre class="screen">./stream.sh -p 9103 -i uppercaseWords -c print
./stream.sh -p 9102 -i words -f uppercase -o uppercaseWords
./stream.sh -p 9101 -s wordstream -o words</pre><p>The output will appear in the console of the sink app (one message per second, converted to uppercase):</p><pre class="screen">MESSAGE-0
MESSAGE-1
MESSAGE-2
MESSAGE-3
MESSAGE-4
MESSAGE-5
MESSAGE-6
MESSAGE-7
MESSAGE-8
MESSAGE-9
...</pre></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_serverless_platform_adapters" href="#_serverless_platform_adapters"></a>132.&nbsp;Serverless Platform Adapters</h2></div></div></div><p>As well as being able to run as a standalone process, a Spring Cloud
Function application can be adapted to run one of the existing
serverless platforms. In the project there are adapters for
<a class="link" href="https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-aws" target="_top">AWS
Lambda</a>,
<a class="link" href="https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-azure" target="_top">Azure</a>,
and
<a class="link" href="https://github.com/spring-cloud/spring-cloud-function/tree/master/spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk" target="_top">Apache
OpenWhisk</a>. The <a class="link" href="https://github.com/fnproject/fn" target="_top">Oracle Fn platform</a>
has its own Spring Cloud Function adapter. And
<a class="link" href="https://projectriff.io" target="_top">Riff</a> supports Java functions and its
<a class="link" href="https://github.com/projectriff/java-function-invoker" target="_top">Java Function
Invoker</a> acts natively is an adapter for Spring Cloud Function jars.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_aws_lambda" href="#_aws_lambda"></a>132.1&nbsp;AWS Lambda</h2></div></div></div><p>The <a class="link" href="https://aws.amazon.com/" target="_top">AWS</a> adapter takes a Spring Cloud Function app and converts it to a form that can run in AWS Lambda.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_introduction_3" href="#_introduction_3"></a>132.1.1&nbsp;Introduction</h3></div></div></div><p>The adapter has a couple of generic request handlers that you can use. The most generic is <code class="literal">SpringBootStreamHandler</code>, which uses a Jackson <code class="literal">ObjectMapper</code> provided by Spring Boot to serialize and deserialize the objects in the function. There is also a <code class="literal">SpringBootRequestHandler</code> which you can extend, and provide the input and output types as type parameters (enabling AWS to inspect the class and do the JSON conversions itself).</p><p>If your app has more than one <code class="literal">@Bean</code> of type <code class="literal">Function</code> etc. then you can choose the one to use by configuring <code class="literal">function.name</code> (e.g. as <code class="literal">FUNCTION_NAME</code> environment variable in AWS). The functions are extracted from the Spring Cloud <code class="literal">FunctionCatalog</code> (searching first for <code class="literal">Function</code> then <code class="literal">Consumer</code> and finally <code class="literal">Supplier</code>).</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_notes_on_jar_layout" href="#_notes_on_jar_layout"></a>132.1.2&nbsp;Notes on JAR Layout</h3></div></div></div><p>You don&#8217;t need the Spring Cloud Function Web or Stream adapter at runtime in Lambda, so you might need to exclude those before you create the JAR you send to AWS. A Lambda application has to be shaded, but a Spring Boot standalone application does not, so you can run the same app using 2 separate jars (as per the sample). The sample app creates 2 jar files, one with an <code class="literal">aws</code> classifier for deploying in Lambda, and one executable (thin) jar that includes <code class="literal">spring-cloud-function-web</code> at runtime. Spring Cloud Function will try and locate a "main class" for you from the JAR file manifest, using the <code class="literal">Start-Class</code> attribute (which will be added for you by the Spring Boot tooling if you use the starter parent). If there is no <code class="literal">Start-Class</code> in your manifest you can use an environment variable <code class="literal">MAIN_CLASS</code> when you deploy the function to AWS.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_upload" href="#_upload"></a>132.1.3&nbsp;Upload</h3></div></div></div><p>Build the sample under <code class="literal">spring-cloud-function-samples/function-sample-aws</code> and upload the <code class="literal">-aws</code> jar file to Lambda. The handler can be <code class="literal">example.Handler</code> or <code class="literal">org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler</code> (FQN of the class, <span class="emphasis"><em>not</em></span> a method reference, although Lambda does accept method references).</p><pre class="screen">./mvnw -U clean package</pre><p>Using the AWS command line tools it looks like this:</p><pre class="screen">aws lambda create-function --function-name Uppercase --role arn:aws:iam::[USERID]:role/service-role/[ROLE] --zip-file fileb://function-sample-aws/target/function-sample-aws-2.0.0.BUILD-SNAPSHOT-aws.jar --handler org.springframework.cloud.function.adapter.aws.SpringBootStreamHandler --description "Spring Cloud Function Adapter Example" --runtime java8 --region us-east-1 --timeout 30 --memory-size 1024 --publish</pre><p>The input type for the function in the AWS sample is a Foo with a single property called "value". So you would need this to test it:</p><pre class="screen">{
"value": "test"
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The AWS sample app is written in the "functional" style (as an <code class="literal">ApplicationContextInitializer</code>). This is much faster on startup in Lambda than the traditional <code class="literal">@Bean</code> style, so if you don&#8217;t need <code class="literal">@Beans</code> (or <code class="literal">@EnableAutoConfiguration</code>) it&#8217;s a good choice. Warm starts are not affected.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_platfom_specific_features" href="#_platfom_specific_features"></a>132.1.4&nbsp;Platfom Specific Features</h3></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_http_and_api_gateway" href="#_http_and_api_gateway"></a>HTTP and API Gateway</h4></div></div></div><p>AWS has some platform-specific data types, including batching of messages, which is much more efficient than processing each one individually. To make use of these types you can write a function that depends on those types. Or you can rely on Spring to extract the data from the AWS types and convert it to a Spring <code class="literal">Message</code>. To do this you tell AWS that the function is of a specific generic handler type (depending on the AWS service) and provide a bean of type <code class="literal">Function&lt;Message&lt;S&gt;,Message&lt;T&gt;&gt;</code>, where <code class="literal">S</code> and <code class="literal">T</code> are your business data types. If there is more than one bean of type <code class="literal">Function</code> you may also need to configure the Spring Boot property <code class="literal">function.name</code> to be the name of the target bean (e.g. use <code class="literal">FUNCTION_NAME</code> as an environment variable).</p><p>The supported AWS services and generic handler types are listed below:</p><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"><col class="col_4"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Service</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">AWS Types</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Generic Handler</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</th></tr></thead><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>API Gateway</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">APIGatewayProxyRequestEvent</code>, <code class="literal">APIGatewayProxyResponseEvent</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p><code class="literal">org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler</code></p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Kinesis</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>KinesisEvent</p></td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>org.springframework.cloud.function.adapter.aws.SpringBootKinesisEventHandler</p></td><td style="" align="left" valign="top">&nbsp;</td></tr></tbody></table></div><p>For example, to deploy behind an API Gateway, use <code class="literal">--handler org.springframework.cloud.function.adapter.aws.SpringBootApiGatewayRequestHandler</code> in your AWS command line (in via the UI) and define a <code class="literal">@Bean</code> of type <code class="literal">Function&lt;Message&lt;Foo&gt;,Message&lt;Bar&gt;&gt;</code> where <code class="literal">Foo</code> and <code class="literal">Bar</code> are POJO types (the data will be marshalled and unmarshalled by AWS using Jackson).</p></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_azure_functions" href="#_azure_functions"></a>132.2&nbsp;Azure Functions</h2></div></div></div><p>The <a class="link" href="https://azure.microsoft.com" target="_top">Azure</a> adapter bootstraps a Spring Cloud Function context and channels function calls from the Azure framework into the user functions, using Spring Boot configuration where necessary. Azure Functions has quite a unique, but invasive programming model, involving annotations in user code that are specific to the platform. The easiest way to use it with Spring Cloud is to extend a base class and write a method in it with the <code class="literal">@FunctionName</code> annotation which delegates to a base class method.</p><p>This project provides an adapter layer for a Spring Cloud Function application onto Azure.
You can write an app with a single <code class="literal">@Bean</code> of type <code class="literal">Function</code> and it will be deployable in Azure if you get the JAR file laid out right.</p><p>There is an <code class="literal">AzureSpringBootRequestHandler</code> which you must extend, and provide the input and output types as annotated method parameters (enabling Azure to inspect the class and create JSON bindings). The base class has two useful methods (<code class="literal">handleRequest</code> and <code class="literal">handleOutput</code>) to which you can delegate the actual function call, so mostly the function will only ever have one line.</p><p>Example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> FooHandler <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AzureSpringBootRequestHandler&lt;Foo, Bar&gt; {
<em><span class="hl-annotation" style="color: gray">@FunctionName("uppercase")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Bar execute(
<em><span class="hl-annotation" style="color: gray">@HttpTrigger(name = "req", methods = { HttpMethod.GET,
HttpMethod.POST }, authLevel = AuthorizationLevel.ANONYMOUS)</span></em>
Foo foo,
ExecutionContext context) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> handleRequest(foo, context);
}
}</pre><p>This Azure handler will delegate to a <code class="literal">Function&lt;Foo,Bar&gt;</code> bean (or a <code class="literal">Function&lt;Publisher&lt;Foo&gt;,Publisher&lt;Bar&gt;&gt;</code>). Some Azure triggers (e.g. <code class="literal">@CosmosDBTrigger</code>) result in a input type of <code class="literal">List</code> and in that case you can bind to <code class="literal">List</code> in the Azure handler, or <code class="literal">String</code> (the raw JSON). The <code class="literal">List</code> input delegates to a <code class="literal">Function</code> with input type <code class="literal">Map&lt;String,Object&gt;</code>, or <code class="literal">Publisher</code> or <code class="literal">List</code> of the same type. The output of the <code class="literal">Function</code> can be a <code class="literal">List</code> (one-for-one) or a single value (aggregation), and the output binding in the Azure declaration should match.</p><p>If your app has more than one <code class="literal">@Bean</code> of type <code class="literal">Function</code> etc. then you can choose the one to use by configuring <code class="literal">function.name</code>. Or if you make the <code class="literal">@FunctionName</code> in the Azure handler method match the function name it should work that way (also for function apps with multiple functions). The functions are extracted from the Spring Cloud <code class="literal">FunctionCatalog</code> so the default function names are the same as the bean names.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_notes_on_jar_layout_2" href="#_notes_on_jar_layout_2"></a>132.2.1&nbsp;Notes on JAR Layout</h3></div></div></div><p>You don&#8217;t need the Spring Cloud Function Web at runtime in Azure, so you can exclude this before you create the JAR you deploy to Azure, but it won&#8217;t be used if you include it so it doesn&#8217;t hurt to leave it in. A function application on Azure is an archive generated by the Maven plugin. The function lives in the JAR file generated by this project. The sample creates it as an executable jar, using the thin layout, so that Azure can find the handler classes. If you prefer you can just use a regular flat JAR file. The dependencies should <span class="strong"><strong>not</strong></span> be included.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_build" href="#_build"></a>132.2.2&nbsp;Build</h3></div></div></div><pre class="screen">./mvnw -U clean package</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_running_the_sample" href="#_running_the_sample"></a>132.2.3&nbsp;Running the sample</h3></div></div></div><p>You can run the sample locally, just like the other Spring Cloud Function samples:</p><p></p><p></p><p>and <code class="literal">curl -H "Content-Type: text/plain" localhost:8080/function -d '{"value": "hello foobar"}'</code>.</p><p>You will need the <code class="literal">az</code> CLI app (see <a class="link" href="https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven" target="_top">https://docs.microsoft.com/en-us/azure/azure-functions/functions-create-first-java-maven</a> for more detail). To deploy the function on Azure runtime:</p><pre class="screen">$ az login
$ mvn azure-functions:deploy</pre><p>On another terminal try this: <code class="literal">curl <a class="link" href="https://<azure-function-url-from-the-log&gt;/api/uppercase" target="_top">https://&lt;azure-function-url-from-the-log&gt;/api/uppercase</a> -d '{"value": "hello foobar!"}'</code>. Please ensure that you use the right URL for the function above. Alternatively you can test the function in the Azure Dashboard UI (click on the function name, go to the right hand side and click "Test" and to the bottom right, "Run").</p><p>The input type for the function in the Azure sample is a Foo with a single property called "value". So you need this to test it with something like below:</p><pre class="screen">{
"value": "foobar"
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The Azure sample app is written in the "non-functional" style (using <code class="literal">@Bean</code>). The functional style (with just <code class="literal">Function</code> or <code class="literal">ApplicationContextInitializer</code>) is much faster on startup in Azure than the traditional <code class="literal">@Bean</code> style, so if you don&#8217;t need <code class="literal">@Beans</code> (or <code class="literal">@EnableAutoConfiguration</code>) it&#8217;s a good choice. Warm starts are not affected.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_apache_openwhisk" href="#_apache_openwhisk"></a>132.3&nbsp;Apache Openwhisk</h2></div></div></div><p>The <a class="link" href="https://openwhisk.apache.org/" target="_top">OpenWhisk</a> adapter is in the form of an executable jar that can be used in a a docker image to be deployed to Openwhisk. The platform works in request-response mode, listening on port 8080 on a specific endpoint, so the adapter is a simple Spring MVC application.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_quick_start_5" href="#_quick_start_5"></a>132.3.1&nbsp;Quick Start</h3></div></div></div><p>Implement a POF (be sure to use the <code class="literal">functions</code> package):</p><pre class="screen">package functions;
import java.util.function.Function;
public class Uppercase implements Function&lt;String, String&gt; {
public String apply(String input) {
return input.toUpperCase();
}
}</pre><p>Install it into your local Maven repository:</p><pre class="screen">./mvnw clean install</pre><p>Create a <code class="literal">function.properties</code> file that provides its Maven coordinates. For example:</p><pre class="screen">dependencies.function: com.example:pof:0.0.1-SNAPSHOT</pre><p>Copy the openwhisk runner JAR to the working directory (same directory as the properties file):</p><pre class="screen">cp spring-cloud-function-adapters/spring-cloud-function-adapter-openwhisk/target/spring-cloud-function-adapter-openwhisk-2.0.0.BUILD-SNAPSHOT.jar runner.jar</pre><p>Generate a m2 repo from the <code class="literal">--thin.dryrun</code> of the runner JAR with the above properties file:</p><pre class="screen">java -jar -Dthin.root=m2 runner.jar --thin.name=function --thin.dryrun</pre><p>Use the following Dockerfile:</p><pre class="screen">FROM openjdk:8-jdk-alpine
VOLUME /tmp
COPY m2 /m2
ADD runner.jar .
ADD function.properties .
ENV JAVA_OPTS=""
ENTRYPOINT [ "java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "runner.jar", "--thin.root=/m2", "--thin.name=function", "--function.name=uppercase"]
EXPOSE 8080</pre><div class="blockquote"><blockquote class="blockquote"><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>you could use a Spring Cloud Function app, instead of just a jar with a POF in it, in which case you would have to change the way the app runs in the container so that it picks up the main class as a source file. For example, you could change the <code class="literal">ENTRYPOINT</code> above and add <code class="literal">--spring.main.sources=com.example.SampleApplication</code>.</p></td></tr></table></div></blockquote></div><p>Build the Docker image:</p><pre class="screen">docker build -t [username/appname] .</pre><p>Push the Docker image:</p><pre class="screen">docker push [username/appname]</pre><p>Use the OpenWhisk CLI (e.g. after <code class="literal">vagrant ssh</code>) to create the action:</p><pre class="screen">wsk action create example --docker [username/appname]</pre><p>Invoke the action:</p><pre class="screen">wsk action invoke example --result --param payload foo
{
"result": "FOO"
}</pre></div></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_spring_cloud_kubernetes" href="#_spring_cloud_kubernetes"></a>Part&nbsp;XVII.&nbsp;Spring Cloud Kubernetes</h1></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_why_do_you_need_spring_cloud_kubernetes" href="#_why_do_you_need_spring_cloud_kubernetes"></a>133.&nbsp;Why do you need Spring Cloud Kubernetes?</h2></div></div></div><p>Spring Cloud Kubernetes provide Spring Cloud common interfaces implementations to consume Kubernetes native services.
The main objective of the projects provided in this repository is to facilitate the integration of Spring Cloud/Spring Boot applications running inside Kubernetes.</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_discoveryclient_for_kubernetes" href="#_discoveryclient_for_kubernetes"></a>Part&nbsp;XVIII.&nbsp;DiscoveryClient for Kubernetes</h1></div></div></div><div class="partintro"><div></div><p>This project provides an implementation of <a class="link" href="https://github.com/spring-cloud/spring-cloud-commons/blob/master/spring-cloud-commons/src/main/java/org/springframework/cloud/client/discovery/DiscoveryClient.java" target="_top">Discovery Client</a>
for <a class="link" href="http://kubernetes.io" target="_top">Kubernetes</a>.
This allows you to query Kubernetes endpoints <span class="strong"><strong>(see <a class="link" href="http://kubernetes.io/docs/user-guide/services/" target="_top">services</a>)</strong></span> by name.
A service is typically exposed by the Kubernetes API server as a collection of endpoints which represent <code class="literal">http</code>, <code class="literal">https</code> addresses that a client can
access from a Spring Boot application running as a pod. This discovery feature is also used by the Spring Cloud Kubernetes Ribbon project
to fetch the list of the endpoints defined for an application to be load balanced.</p><p>This is something that you get for free just by adding the following dependency inside your project:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-kubernetes<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${latest.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>To enable loading of the <code class="literal">DiscoveryClient</code>, add <code class="literal">@EnableDiscoveryClient</code> to the according configuration or application class like this:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableDiscoveryClient</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre><p>Then you can inject the client in your code simply by:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> DiscoveryClient discoveryClient;</pre><p>If for any reason you need to disable the <code class="literal">DiscoveryClient</code> you can simply set the following property in <code class="literal">application.properties</code>:</p><pre class="screen">spring.cloud.kubernetes.discovery.enabled=false</pre><p>Some Spring Cloud components use the <code class="literal">DiscoveryClient</code> in order to obtain info about the local service instance. For
this to work you need to align the Kubernetes service name with the <code class="literal">spring.application.name</code> property.</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_kubernetes_propertysource_implementations" href="#_kubernetes_propertysource_implementations"></a>Part&nbsp;XIX.&nbsp;Kubernetes PropertySource implementations</h1></div></div></div><div class="partintro"><div></div><p>The most common approach to configure your Spring Boot application is to create an <code class="literal">application.properties|yaml</code> or
an <code class="literal">application-profile.properties|yaml</code> file containing key-value pairs providing customization values to your
application or Spring Boot starters. Users may override these properties by specifying system properties or environment
variables.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_configmap_propertysource" href="#_configmap_propertysource"></a>134.&nbsp;ConfigMap PropertySource</h2></div></div></div><p>Kubernetes provides a resource named <a class="link" href="http://kubernetes.io/docs/user-guide/configmap/" target="_top">ConfigMap</a> to externalize the
parameters to pass to your application in the form of key-value pairs or embedded <code class="literal">application.properties|yaml</code> files.
The <a class="link" href="./spring-cloud-kubernetes-config" target="_top">Spring Cloud Kubernetes Config</a> project makes Kubernetes `ConfigMap`s available
during application bootstrapping and triggers hot reloading of beans or Spring context when changes are detected on
observed `ConfigMap`s.</p><p>The default behavior is to create a <code class="literal">ConfigMapPropertySource</code> based on a Kubernetes <code class="literal">ConfigMap</code> which has <code class="literal">metadata.name</code> of either the name of
your Spring application (as defined by its <code class="literal">spring.application.name</code> property) or a custom name defined within the
<code class="literal">bootstrap.properties</code> file under the following key <code class="literal">spring.cloud.kubernetes.config.name</code>.</p><p>However, more advanced configuration are possible where multiple ConfigMaps can be used
This is made possible by the <code class="literal">spring.cloud.kubernetes.config.sources</code> list.
For example one could define the following ConfigMaps</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: cloud-k8s-app
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> kubernetes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: default-name
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> namespace</span>: default-namespace
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> sources</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Spring Cloud Kubernetes will lookup a ConfigMap named c1 in namespace default-namespace</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: c1
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Spring Cloud Kubernetes will lookup a ConfigMap named default-name in whatever namespace n2</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - namespace</span>: n2
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment"># Spring Cloud Kubernetes will lookup a ConfigMap named c3 in namespace n3</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - namespace</span>: n3
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: c3</pre><p>In the example above, it <code class="literal">spring.cloud.kubernetes.config.namespace</code> had not been set,
then the ConfigMap named <code class="literal">c1</code> would be looked up in the namespace that the application runs</p><p>Any matching <code class="literal">ConfigMap</code> that is found, will be processed as follows:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">apply individual configuration properties.</li><li class="listitem">apply as <code class="literal">yaml</code> the content of any property named <code class="literal">application.yaml</code></li><li class="listitem">apply as properties file the content of any property named <code class="literal">application.properties</code></li></ul></div><p>The single exception to the aforementioned flow is when the <code class="literal">ConfigMap</code> contains a <span class="strong"><strong>single</strong></span> key that indicates
the file is a YAML or Properties file. In that case the name of the key does NOT have to be <code class="literal">application.yaml</code> or
<code class="literal">application.properties</code> (it can be anything) and the value of the property will be treated correctly.
This features facilitates the use case where the <code class="literal">ConfigMap</code> was created using something like:</p><p><code class="literal">kubectl create configmap game-config --from-file=/path/to/app-config.yaml</code></p><p>Example:</p><p>Let&#8217;s assume that we have a Spring Boot application named <code class="literal">demo</code> that uses properties to read its thread pool
configuration.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">pool.size.core</code></li><li class="listitem"><code class="literal">pool.size.maximum</code></li></ul></div><p>This can be externalized to config map in <code class="literal">yaml</code> format:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">kind</span>: ConfigMap
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">apiVersion</span>: v1
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">metadata</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: demo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">data</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pool.size.core</span>: <span class="hl-number">1</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> pool.size.max</span>: <span class="hl-number">16</span></pre><p>Individual properties work fine for most cases but sometimes embedded <code class="literal">yaml</code> is more convenient. In this case we will
use a single property named <code class="literal">application.yaml</code> to embed our <code class="literal">yaml</code>:</p><pre class="literallayout"> ```yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
application.yaml: |-
pool:
size:
core: 1
max:16</pre><pre class="screen">The following also works:
```yaml
kind: ConfigMap
apiVersion: v1
metadata:
name: demo
data:
custom-name.yaml: |-
pool:
size:
core: 1
max:16</pre><p>Spring Boot applications can also be configured differently depending on active profiles which will be merged together
when the ConfigMap is read. It is possible to provide different property values for different profiles using an
<code class="literal">application.properties|yaml</code> property, specifying profile-specific values each in their own document
(indicated by the <code class="literal">---</code> sequence) as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">kind</span>: ConfigMap
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">apiVersion</span>: v1
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">metadata</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: demo
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">data</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application.yml</span>: |-
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> greeting</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Hello to the World
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> farewell</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Goodbye
---
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>: development
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> greeting</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Hello to the Developers
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> farewell</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Goodbye to the Developers
---
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>: production
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> greeting</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Hello to the Ops</pre><p>In the above case, the configuration loaded into your Spring Application with the <code class="literal">development</code> profile will be:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> greeting</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Hello to the Developers
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> farewell</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Goodbye to the Developers</pre><p>whereas if the <code class="literal">production</code> profile is active, the configuration will be:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> greeting</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Hello to the Ops
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> farewell</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> message</span>: Say Goodbye</pre><p>If both profiles are active, the property which appears last within the configmap will overwrite preceding values.</p><p>To tell to Spring Boot which <code class="literal">profile</code> should be enabled at bootstrap, a system property can be passed to the Java
command launching your Spring Boot application using an env variable that you will define with the OpenShift
<code class="literal">DeploymentConfig</code> or Kubernetes <code class="literal">ReplicationConfig</code> resource file as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">apiVersion</span>: v1
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">kind</span>: DeploymentConfig
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spec</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> replicas</span>: <span class="hl-number">1</span>
...
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> spec</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> containers</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - env</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: JAVA_APP_DIR
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> value</span>: /deployments
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: JAVA_OPTIONS
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> value</span>: -Dspring.profiles.active=developer</pre><p><span class="strong"><strong>Notes:</strong></span>
- check the security configuration section, to access config maps from inside a pod you need to have the correct
Kubernetes service accounts, roles and role bindings.</p><div class="table"><a name="d0e34410" href="#d0e34410"></a><p class="title"><b>Table&nbsp;134.1.&nbsp;Properties:</b></p><div class="table-contents"><table summary="Properties:" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"><col class="col_4"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Name</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Type</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Default</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Description</th></tr></thead><tfoot><tr><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.config.enableApi</p></th><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Boolean</p></th><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>true</p></th><th style="" align="left" valign="top"><p>Enable/Disable consuming ConfigMaps via APIs</p></th></tr></tfoot><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.config.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Boolean</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable Secrets PropertySource</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.config.name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>String</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>${spring.application.name}</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Sets the name of ConfigMap to lookup</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.config.namespace</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>String</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Client namespace</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Sets the Kubernetes namespace where to lookup</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.config.paths</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>null</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Sets the paths where ConfigMaps are mounted</p></td></tr></tbody></table></div></div><br class="table-break"></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_secrets_propertysource" href="#_secrets_propertysource"></a>135.&nbsp;Secrets PropertySource</h2></div></div></div><p>Kubernetes has the notion of [Secrets](<a class="link" href="https://kubernetes.io/docs/concepts/configuration/secret/" target="_top">https://kubernetes.io/docs/concepts/configuration/secret/</a>) for storing
sensitive data such as password, OAuth tokens, etc. This project provides integration with <code class="literal">Secrets</code> to make secrets
accessible by Spring Boot applications. This feature can be explicitly enabled/disabled using the <code class="literal">spring.cloud.kubernetes.secrets.enabled</code> property.</p><p>The <code class="literal">SecretsPropertySource</code> when enabled will lookup Kubernetes for <code class="literal">Secrets</code> from the following sources:
1. reading recursively from secrets mounts
2. named after the application (as defined by <code class="literal">spring.application.name</code>)
3. matching some labels</p><p>Please note that by default, consuming Secrets via API (points 2 and 3 above) <span class="strong"><strong>is not enabled</strong></span> for security reasons
and it is recommend that containers share secrets via mounted volumes.</p><p>If the secrets are found their data is made available to the application.</p><p><span class="strong"><strong>Example:</strong></span></p><p>Let&#8217;s assume that we have a spring boot application named <code class="literal">demo</code> that uses properties to read its database
configuration. We can create a Kubernetes secret using the following command:</p><pre class="screen">oc create secret generic db-secret --from-literal=username=user --from-literal=password=p455w0rd</pre><p>This would create the following secret (shown using <code class="literal">oc get secrets db-secret -o yaml</code>):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">apiVersion</span>: v1
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">data</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> password</span>: cDQ1NXcwcmQ=
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> username</span>: dXNlcg==
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">kind</span>: Secret
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">metadata</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> creationTimestamp</span>: <span class="hl-number">2017</span>-<span class="hl-number">07</span>-<span class="hl-number">04</span>T09:<span class="hl-number">15</span>:<span class="hl-number">57</span>Z
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: db-secret
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> namespace</span>: default
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> resourceVersion</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"357496"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> selfLink</span>: /api/v1/namespaces/default/secrets/db-secret
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uid</span>: <span class="hl-number">63</span>c89263-<span class="hl-number">6099</span>-<span class="hl-number">11e7</span>-b3da-<span class="hl-number">76d</span>6186905a8
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">type</span>: Opaque</pre><p>Note that the data contains Base64-encoded versions of the literal provided by the create command.</p><p>This secret can then be used by your application for example by exporting the secret&#8217;s value as environment variables:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">apiVersion</span>: v1
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">kind</span>: Deployment
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">metadata</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: ${project.artifactId<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spec</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> template</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> spec</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> containers</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - env</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: DB_USERNAME
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> valueFrom</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> secretKeyRef</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: db-secret
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key</span>: username
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: DB_PASSWORD
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> valueFrom</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> secretKeyRef</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: db-secret
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> key</span>: password</pre><p>You can select the Secrets to consume in a number of ways:</p><div class="orderedlist"><ol class="orderedlist" type="1"><li class="listitem"><p class="simpara">By listing the directories where secrets are mapped:
<code class="literal">`
-Dspring.cloud.kubernetes.secrets.paths=/etc/secrets/db-secret,etc/secrets/postgresql
</code>`</p><pre class="literallayout">If you have all the secrets mapped to a common root, you can set them like:</pre><pre class="literallayout">```
-Dspring.cloud.kubernetes.secrets.paths=/etc/secrets
```</pre></li><li class="listitem">By setting a named secret:
<code class="literal">`
-Dspring.cloud.kubernetes.secrets.name=db-secret
</code>`</li><li class="listitem">By defining a list of labels:
<code class="literal">`
-Dspring.cloud.kubernetes.secrets.labels.broker=activemq
-Dspring.cloud.kubernetes.secrets.labels.db=postgresql
</code>`</li></ol></div><div class="table"><a name="d0e34575" href="#d0e34575"></a><p class="title"><b>Table&nbsp;135.1.&nbsp;Properties:</b></p><div class="table-contents"><table summary="Properties:" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"><col class="col_4"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Name</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Type</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Default</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Description</th></tr></thead><tfoot><tr><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.secrets.enableApi</p></th><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Boolean</p></th><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>false</p></th><th style="" align="left" valign="top"><p>Enable/Disable consuming secrets via APIs (examples 2 and 3)</p></th></tr></tfoot><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.secrets.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Boolean</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable Secrets PropertySource</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.secrets.name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>String</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>${spring.application.name}</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Sets the name of the secret to lookup</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.secrets.namespace</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>String</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Client namespace</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Sets the Kubernetes namespace where to lookup</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.secrets.labels</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Map</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>null</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Sets the labels used to lookup secrets</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.secrets.paths</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>null</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Sets the paths where secrets are mounted (example 1)</p></td></tr></tbody></table></div></div><br class="table-break"><p><span class="strong"><strong>Notes:</strong></span>
- The property <code class="literal">spring.cloud.kubernetes.secrets.labels</code> behaves as defined by
<a class="link" href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding#map-based-binding" target="_top">Map-based binding</a>.
- The property <code class="literal">spring.cloud.kubernetes.secrets.paths</code> behaves as defined by
<a class="link" href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-Configuration-Binding#collection-based-binding" target="_top">Collection-based binding</a>.
- Access to secrets via API may be restricted for security reasons, the preferred way is to mount secret to the POD.</p><p>Example of application using secrets (though it hasn&#8217;t been updated to use the new <code class="literal">spring-cloud-kubernetes</code> project):
<a class="link" href="https://github.com/fabric8-quickstarts/spring-boot-camel-config" target="_top">spring-boot-camel-config</a></p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_propertysource_reload" href="#_propertysource_reload"></a>136.&nbsp;PropertySource Reload</h2></div></div></div><p>Some applications may need to detect changes on external property sources and update their internal status to reflect the new configuration.
The reload feature of Spring Cloud Kubernetes is able to trigger an application reload when a related <code class="literal">ConfigMap</code> or
<code class="literal">Secret</code> changes.</p><p>This feature is disabled by default and can be enabled using the configuration property <code class="literal">spring.cloud.kubernetes.reload.enabled=true</code>
(eg. in the <span class="strong"><strong>application.properties</strong></span> file).</p><p>The following levels of reload are supported (property <code class="literal">spring.cloud.kubernetes.reload.strategy</code>):
- <span class="strong"><strong><code class="literal">refresh</code> (default)</strong></span>: only configuration beans annotated with <code class="literal">@ConfigurationProperties</code> or <code class="literal">@RefreshScope</code> are reloaded.
This reload level leverages the refresh feature of Spring Cloud Context.
- <span class="strong"><strong><code class="literal">restart_context</code></strong></span>: the whole Spring <span class="emphasis"><em>ApplicationContext</em></span> is gracefully restarted. Beans are recreated with the new configuration.
- <span class="strong"><strong><code class="literal">shutdown</code></strong></span>: the Spring <span class="emphasis"><em>ApplicationContext</em></span> is shut down to activate a restart of the container.
When using this level, make sure that the lifecycle of all non-daemon threads is bound to the ApplicationContext
and that a replication controller or replica set is configured to restart the pod.</p><p>Example:</p><p>Assuming that the reload feature is enabled with default settings (<span class="strong"><strong><code class="literal">refresh</code></strong></span> mode), the following bean will be refreshed when the config map changes:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@ConfigurationProperties(prefix = "bean")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyConfig {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> String message = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"a message that can be changed live"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// getter and setters</span>
}</pre><p>A way to see that changes effectively happen is creating another bean that prints the message periodically.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Component</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyBean {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MyConfig config;
<em><span class="hl-annotation" style="color: gray">@Scheduled(fixedDelay = 5000)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> hello() {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"The message is: "</span> + config.getMessage());
}
}</pre><p>The message printed by the application can be changed using a <code class="literal">ConfigMap</code> as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">apiVersion</span>: v1
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">kind</span>: ConfigMap
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">metadata</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: reload-example
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">data</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application.properties</span>: |-
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bean.message</span>=Hello World!</pre><p>Any change to the property named <code class="literal">bean.message</code> in the <code class="literal">ConfigMap</code> associated to the pod will be reflected in the
output. More generally speaking, changes associated to properties prefixed with the value defined by the <code class="literal">prefix</code>
field of the <code class="literal">@ConfigurationProperties</code> annotation will be detected and reflected in the application.
[Associating a <code class="literal">ConfigMap</code> to a pod](#configmap-propertysource) is explained above.</p><p>The full example is available in [spring-cloud-kubernetes-reload-example](spring-cloud-kubernetes-examples/kubernetes-reload-example).</p><p>The reload feature supports two operating modes:
- <span class="strong"><strong>event (default)</strong></span>: watches for changes in config maps or secrets using the Kubernetes API (web socket).
Any event will produce a re-check on the configuration and a reload in case of changes.
The <code class="literal">view</code> role on the service account is required in order to listen for config map changes. A higher level role (eg. <code class="literal">edit</code>) is required for secrets
(secrets are not monitored by default).
- <span class="strong"><strong>polling</strong></span>: re-creates the configuration periodically from config maps and secrets to see if it has changed.
The polling period can be configured using the property <code class="literal">spring.cloud.kubernetes.reload.period</code> and defaults to <span class="strong"><strong>15 seconds</strong></span>.
It requires the same role as the monitored property source.
This means, for example, that using polling on file mounted secret sources does not require particular privileges.</p><div class="table"><a name="d0e34805" href="#d0e34805"></a><p class="title"><b>Table&nbsp;136.1.&nbsp;Properties:</b></p><div class="table-contents"><table summary="Properties:" style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"><col class="col_4"></colgroup><thead><tr><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Name</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Type</th><th style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">Default</th><th style="border-bottom: 0.5pt solid ; " align="left" valign="top">Description</th></tr></thead><tfoot><tr><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.reload.period</p></th><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>Long</p></th><th style="border-right: 0.5pt solid ; " align="left" valign="top"><p>15000</p></th><th style="" align="left" valign="top"><p>The period in milliseconds for verifying changes when using the <span class="strong"><strong>polling</strong></span> strategy</p></th></tr></tfoot><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.reload.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Boolean</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables monitoring of property sources and configuration reload</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.reload.monitoring-config-maps</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Boolean</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Allow monitoring changes in config maps</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.reload.monitoring-secrets</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Boolean</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Allow monitoring changes in secrets</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.reload.strategy</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enum</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>refresh</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The strategy to use when firing a reload (<span class="strong"><strong>refresh</strong></span>, <span class="strong"><strong>restart_context</strong></span>, <span class="strong"><strong>shutdown</strong></span>)</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.kubernetes.reload.mode</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enum</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>event</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Specifies how to listen for changes in property sources (<span class="strong"><strong>event</strong></span>, <span class="strong"><strong>polling</strong></span>)</p></td></tr></tbody></table></div></div><br class="table-break"><p><span class="strong"><strong>Notes</strong></span>:
- Properties under <span class="strong"><strong>spring.cloud.kubernetes.reload.</strong></span> should not be used in config maps or secrets: changing such properties at runtime may lead to unexpected results;
- Deleting a property or the whole config map does not restore the original state of the beans when using the <span class="strong"><strong>refresh</strong></span> level.</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_ribbon_discovery_in_kubernetes" href="#_ribbon_discovery_in_kubernetes"></a>Part&nbsp;XX.&nbsp;Ribbon discovery in Kubernetes</h1></div></div></div><div class="partintro"><div></div><p>Spring Cloud client applications calling a microservice should be interested on relying on a client load-balancing
feature in order to automatically discover at which endpoint(s) it can reach a given service. This mechanism has been
implemented within the [spring-cloud-kubernetes-ribbon](spring-cloud-kubernetes-ribbon/pom.xml) project where a
Kubernetes client will populate a <a class="link" href="https://github.com/Netflix/ribbon" target="_top">Ribbon</a> <code class="literal">ServerList</code> containing information
about such endpoints.</p><p>The implementation is part of the following starter that you can use by adding its dependency to your pom file:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-kubernetes-netflix<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${latest.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>When the list of the endpoints is populated, the Kubernetes client will search the registered endpoints living in
the current namespace/project matching the service name defined using the Ribbon Client annotation:</p><pre class="programlisting">@RibbonClient(name = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name-service"</span>)</pre><p>You can configure Ribbon&#8217;s behavior by providing properties in your <code class="literal">application.properties</code> (via your application&#8217;s
dedicated <code class="literal">ConfigMap</code>) using the following format: <code class="literal">&lt;name of your service&gt;.ribbon.&lt;Ribbon configuration key&gt;</code> where:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">&lt;name of your service&gt;</code> corresponds to the service name you&#8217;re accessing over Ribbon, as configured using the
<code class="literal">@RibbonClient</code> annotation (e.g. <code class="literal">name-service</code> in the example above)</li><li class="listitem"><code class="literal">&lt;Ribbon configuration key&gt;</code> is one of the Ribbon configuration key defined by
<a class="link" href="https://github.com/Netflix/ribbon/blob/master/ribbon-core/src/main/java/com/netflix/client/config/CommonClientConfigKey.java" target="_top">Ribbon&#8217;s CommonClientConfigKey class</a></li></ul></div><p>Additionally, the <code class="literal">spring-cloud-kubernetes-ribbon</code> project defines two additional configuration keys to further
control how Ribbon interacts with Kubernetes. In particular, if an endpoint defines multiple ports, the default
behavior is to use the first one found. To select more specifically which port to use, in a multi-port service, use
the <code class="literal">PortName</code> key. If you want to specify in which Kubernetes' namespace the target service should be looked up, use
the <code class="literal">KubernetesNamespace</code> key, remembering in both instances to prefix these keys with your service name and
<code class="literal">ribbon</code> prefix as specified above.</p><p>Examples that are using this module for ribbon discovery are:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="./spring-cloud-kubernetes-examples/kubernetes-circuitbreaker-ribbon-example" target="_top">Spring Cloud Circuitbreaker and Ribbon</a></li><li class="listitem"><a class="link" href="https://github.com/fabric8-quickstarts/spring-boot-ribbon" target="_top">fabric8-quickstarts - Spring Boot - Ribbon</a></li><li class="listitem"><a class="link" href="https://github.com/fabric8io/kubeflix/tree/master/examples/loanbroker/bank" target="_top">Kubeflix - LoanBroker - Bank</a></li></ul></div><p><span class="strong"><strong>Note</strong></span>: The Ribbon discovery client can be disabled by setting this key within the application properties file
<code class="literal">spring.cloud.kubernetes.ribbon.enabled=false</code>.</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_kubernetes_awareness" href="#_kubernetes_awareness"></a>Part&nbsp;XXI.&nbsp;Kubernetes Awareness</h1></div></div></div><div class="partintro"><div></div><p>All of the features described above will work equally well regardless of whether your application is running inside
Kubernetes or not. This is really helpful for development and troubleshooting.
From a development point of view, this is really helpful as you can start your Spring Boot application and debug one
of the modules part of this project. It is not required to deploy it in Kubernetes
as the code of the project relies on the
[Fabric8 Kubernetes Java client](<a class="link" href="https://github.com/fabric8io/kubernetes-client" target="_top">https://github.com/fabric8io/kubernetes-client</a>) which is a fluent DSL able to
communicate using <code class="literal">http</code> protocol to the REST API of Kubernetes Server.</p></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_kubernetes_profile_autoconfiguration" href="#_kubernetes_profile_autoconfiguration"></a>137.&nbsp;Kubernetes Profile Autoconfiguration</h2></div></div></div><p>When the application runs as a pod inside Kubernetes a Spring profile named <code class="literal">kubernetes</code> will automatically get activated.
This allows the developer to customize the configuration, to define beans that will be applied when the Spring Boot application is deployed
within the Kubernetes platform <span class="strong"><strong>(e.g. different dev and prod configuration)</strong></span>.</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_pod_health_indicator" href="#_pod_health_indicator"></a>Part&nbsp;XXII.&nbsp;Pod Health Indicator</h1></div></div></div><div class="partintro"><div></div><p>Spring Boot uses <a class="link" href="https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot-actuator/src/main/java/org/springframework/boot/actuate/health/HealthEndpoint.java" target="_top">HealthIndicator</a> to expose info about the health of an application.
That makes it really useful for exposing health related information to the user and are also a good fit for use as <a class="link" href="https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/" target="_top">readiness probes</a>.</p><p>The Kubernetes health indicator which is part of the core module exposes the following info:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">pod name, ip address, namespace, service account, node name and its ip address</li><li class="listitem">flag that indicates if the Spring Boot application is internal or external to Kubernetes</li></ul></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_leader_election" href="#_leader_election"></a>Part&nbsp;XXIII.&nbsp;Leader Election</h1></div></div></div><div class="partintro"><div></div><p>&lt;TBD&gt;</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_security_configurations_inside_kubernetes" href="#_security_configurations_inside_kubernetes"></a>Part&nbsp;XXIV.&nbsp;Security Configurations inside Kubernetes</h1></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_namespace" href="#_namespace"></a>138.&nbsp;Namespace</h2></div></div></div><p>Most of the components provided in this project need to know the namespace. For Kubernetes (1.3+) the namespace is made available to pod as part of the service account secret and automatically detected by the client.
For earlier version it needs to be specified as an env var to the pod. A quick way to do this is:</p><pre class="literallayout">env:
- name: "KUBERNETES_NAMESPACE"
valueFrom:
fieldRef:
fieldPath: "metadata.namespace"</pre></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_service_account" href="#_service_account"></a>139.&nbsp;Service Account</h2></div></div></div><p>For distros of Kubernetes that support more fine-grained role-based access within the cluster, you need to make sure a pod that runs with spring-cloud-kubernetes has access to the Kubernetes API.
For any service accounts you assign to a deployment/pod, you need to make sure it has the correct roles. For example, you can add <code class="literal">cluster-reader</code> permissions to your <code class="literal">default</code> service account depending on the project you&#8217;re in:</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_examples_2" href="#_examples_2"></a>Part&nbsp;XXV.&nbsp;Examples</h1></div></div></div><div class="partintro"><div></div><p>List of examples using these projects:</p><p>&lt;TBD&gt;</p></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_other_resources" href="#_other_resources"></a>Part&nbsp;XXVI.&nbsp;Other Resources</h1></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_building" href="#_building"></a>140.&nbsp;Building</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_basic_compile_and_test" href="#_basic_compile_and_test"></a>140.1&nbsp;Basic Compile and Test</h2></div></div></div><p>To build the source you will need to install JDK 1.7.</p><p>Spring Cloud uses Maven for most build-related activities, and you
should be able to get off the ground quite quickly by cloning the
project you are interested in and typing</p><pre class="screen">$ ./mvnw install</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>You can also install Maven (&gt;=3.3.3) yourself and run the <code class="literal">mvn</code> command
in place of <code class="literal">./mvnw</code> in the examples below. If you do that you also
might need to add <code class="literal">-P spring</code> if your local Maven settings do not
contain repository declarations for spring pre-release artifacts.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Be aware that you might need to increase the amount of memory
available to Maven by setting a <code class="literal">MAVEN_OPTS</code> environment variable with
a value like <code class="literal">-Xmx512m -XX:MaxPermSize=128m</code>. We try to cover this in
the <code class="literal">.mvn</code> configuration, so if you find you have to do it to make a
build succeed, please raise a ticket to get the settings added to
source control.</p></td></tr></table></div><p>For hints on how to build the project look in <code class="literal">.travis.yml</code> if there
is one. There should be a "script" and maybe "install" command. Also
look at the "services" section to see if any services need to be
running locally (e.g. mongo or rabbit). Ignore the git-related bits
that you might find in "before_install" since they&#8217;re related to setting git
credentials and you already have those.</p><p>The projects that require middleware generally include a
<code class="literal">docker-compose.yml</code>, so consider using
<a class="link" href="http://compose.docker.io/" target="_top">Docker Compose</a> to run the middeware servers
in Docker containers. See the README in the
<a class="link" href="https://github.com/spring-cloud-samples/scripts" target="_top">scripts demo
repository</a> for specific instructions about the common cases of mongo,
rabbit and redis.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If all else fails, build with the command from <code class="literal">.travis.yml</code> (usually
<code class="literal">./mvnw install</code>).</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_documentation" href="#_documentation"></a>140.2&nbsp;Documentation</h2></div></div></div><p>The spring-cloud-build module has a "docs" profile, and if you switch
that on it will try to build asciidoc sources from
<code class="literal">src/main/asciidoc</code>. As part of that process it will look for a
<code class="literal">README.adoc</code> and process it by loading all the includes, but not
parsing or rendering it, just copying it to <code class="literal">${main.basedir}</code>
(defaults to <code class="literal">$../../../..</code>, i.e. the root of the project). If there are
any changes in the README it will then show up after a Maven build as
a modified file in the correct place. Just commit it and push the change.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_working_with_the_code" href="#_working_with_the_code"></a>140.3&nbsp;Working with the code</h2></div></div></div><p>If you don&#8217;t have an IDE preference we would recommend that you use
<a class="link" href="http://www.springsource.com/developer/sts" target="_top">Spring Tools Suite</a> or
<a class="link" href="http://eclipse.org" target="_top">Eclipse</a> when working with the code. We use the
<a class="link" href="http://eclipse.org/m2e/" target="_top">m2eclipse</a> eclipse plugin for maven support. Other IDEs and tools
should also work without issue as long as they use Maven 3.3.3 or better.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_importing_into_eclipse_with_m2eclipse" href="#_importing_into_eclipse_with_m2eclipse"></a>140.3.1&nbsp;Importing into eclipse with m2eclipse</h3></div></div></div><p>We recommend the <a class="link" href="http://eclipse.org/m2e/" target="_top">m2eclipse</a> eclipse plugin when working with
eclipse. If you don&#8217;t already have m2eclipse installed it is available from the "eclipse
marketplace".</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Older versions of m2e do not support Maven 3.3, so once the
projects are imported into Eclipse you will also need to tell
m2eclipse to use the right profile for the projects. If you
see many different errors related to the POMs in the projects, check
that you have an up to date installation. If you can&#8217;t upgrade m2e,
add the "spring" profile to your <code class="literal">settings.xml</code>. Alternatively you can
copy the repository settings from the "spring" profile of the parent
pom into your <code class="literal">settings.xml</code>.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_importing_into_eclipse_without_m2eclipse" href="#_importing_into_eclipse_without_m2eclipse"></a>140.3.2&nbsp;Importing into eclipse without m2eclipse</h3></div></div></div><p>If you prefer not to use m2eclipse you can generate eclipse project metadata using the
following command:</p><pre class="screen">$ ./mvnw eclipse:eclipse</pre><p>The generated eclipse projects can be imported by selecting <code class="literal">import existing projects</code>
from the <code class="literal">file</code> menu.</p></div></div></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_contributing" href="#_contributing"></a>141.&nbsp;Contributing</h2></div></div></div><p>Spring Cloud is released under the non-restrictive Apache 2.0 license,
and follows a very standard Github development process, using Github
tracker for issues and merging pull requests into master. If you want
to contribute even something trivial please do not hesitate, but
follow the guidelines below.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_sign_the_contributor_license_agreement" href="#_sign_the_contributor_license_agreement"></a>141.1&nbsp;Sign the Contributor License Agreement</h2></div></div></div><p>Before we accept a non-trivial patch or pull request we will need you to sign the
<a class="link" href="https://cla.pivotal.io/sign/spring" target="_top">Contributor License Agreement</a>.
Signing the contributor&#8217;s agreement does not grant anyone commit rights to the main
repository, but it does mean that we can accept your contributions, and you will get an
author credit if we do. Active contributors might be asked to join the core team, and
given the ability to merge pull requests.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_code_of_conduct" href="#_code_of_conduct"></a>141.2&nbsp;Code of Conduct</h2></div></div></div><p>This project adheres to the Contributor Covenant <a class="link" href="https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc" target="_top">code of
conduct</a>. By participating, you are expected to uphold this code. Please report
unacceptable behavior to <a class="link" href="mailto:spring-code-of-conduct@pivotal.io" target="_top">spring-code-of-conduct@pivotal.io</a>.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_code_conventions_and_housekeeping" href="#_code_conventions_and_housekeeping"></a>141.3&nbsp;Code Conventions and Housekeeping</h2></div></div></div><p>None of these is essential for a pull request, but they will all help. They can also be
added after the original pull request but before a merge.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Use the Spring Framework code format conventions. If you use Eclipse
you can import formatter settings using the
<code class="literal">eclipse-code-formatter.xml</code> file from the
<a class="link" href="https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml" target="_top">Spring
Cloud Build</a> project. If using IntelliJ, you can use the
<a class="link" href="http://plugins.jetbrains.com/plugin/6546" target="_top">Eclipse Code Formatter
Plugin</a> to import the same file.</li><li class="listitem">Make sure all new <code class="literal">.java</code> files to have a simple Javadoc class comment with at least an
<code class="literal">@author</code> tag identifying you, and preferably at least a paragraph on what the class is
for.</li><li class="listitem">Add the ASF license header comment to all new <code class="literal">.java</code> files (copy from existing files
in the project)</li><li class="listitem">Add yourself as an <code class="literal">@author</code> to the .java files that you modify substantially (more
than cosmetic changes).</li><li class="listitem">Add some Javadocs and, if you change the namespace, some XSD doc elements.</li><li class="listitem">A few unit tests would help a lot as well&#8201;&#8212;&#8201;someone has to do it.</li><li class="listitem">If no-one else is using your branch, please rebase it against the current master (or
other target branch in the main project).</li><li class="listitem">When writing a commit message please follow <a class="link" href="http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html" target="_top">these conventions</a>,
if you are fixing an existing issue please add <code class="literal">Fixes gh-XXXX</code> at the end of the commit
message (where XXXX is the issue number).</li></ul></div></div></div></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_appendix_compendium_of_configuration_properties" href="#_appendix_compendium_of_configuration_properties"></a>Part&nbsp;XXVII.&nbsp;Appendix: Compendium of Configuration Properties</h1></div></div></div><div class="partintro"><div></div><div class="informaltable"><table style="border-collapse: collapse;border-top: 0.5pt solid ; border-bottom: 0.5pt solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><tbody><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Description</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws.paramstore.default-context</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws.paramstore.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Is AWS Parameter Store support enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws.paramstore.fail-fast</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Throw exceptions during config lookup if true, otherwise, log warnings.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws.paramstore.name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Alternative to spring.application.name to use in looking up values in AWS Parameter Store.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws.paramstore.prefix</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/config</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Prefix indicating first level for every property. Value must start with a forward slash followed by a valid path segment or be empty. Defaults to "/config".</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws.paramstore.profile-separator</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>_</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.fail-on-error</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to say that a process should fail if there is an encryption or decryption error.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.key</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>A symmetric key. As a stronger alternative, consider using a keystore.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.key-store.alias</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Alias for a key in the store.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.key-store.location</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Location of the key store file, e.g. classpath:/keystore.jks.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.key-store.password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password that locks the keystore.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.key-store.secret</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Secret protecting the key (defaults to the same as the password).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.rsa.algorithm</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The RSA algorithm to use (DEFAULT or OEAP). Once it is set, do not change it (or existing ciphers will not be decryptable).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.rsa.salt</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>deadbeef</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Salt for the random secret used to encrypt cipher text. Once it is set, do not change it (or existing ciphers will not be decryptable).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.rsa.strong</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that "strong" AES encryption should be used internally. If true, then the GCM algorithm is applied to the AES encrypted bytes. Default is false (in which case "standard" CBC is used instead). Once it is set, do not change it (or existing ciphers will not be decryptable).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>encrypt.salt</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>deadbeef</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>A salt for the symmetric key, in the form of a hex-encoded byte array. As a stronger alternative, consider using a keystore.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>endpoints.zookeeper.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the /zookeeper endpoint to inspect the state of zookeeper.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>eureka.client.healthcheck.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables the Eureka health check handler.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>health.config.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that the config server health indicator should be installed.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>health.config.time-to-live</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Time to live for cached result, in milliseconds. Default 300000 (5 min).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>hystrix.metrics.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable Hystrix metrics polling. Defaults to true.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>hystrix.metrics.polling-interval-ms</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Interval between subsequent polling of metrics. Defaults to 2000 ms.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>hystrix.shareSecurityContext</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables auto-configuration of the Hystrix concurrency strategy plugin hook who will transfer the <code class="literal">SecurityContext</code> from your main thread to the one used by the Hystrix command.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.bindings.cache.time-to-live</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum time that a response can be cached.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.bindings.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the bindings endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.bus-env.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the bus-env endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.bus-refresh.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the bus-refresh endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.channels.cache.time-to-live</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum time that a response can be cached.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.channels.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the channels endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.consul.cache.time-to-live</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum time that a response can be cached.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.consul.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the consul endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.env.post.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable changing the Environment through a POST to /env.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.features.cache.time-to-live</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum time that a response can be cached.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.features.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the features endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.gateway.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the gateway endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.hystrix.config</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Hystrix settings. These are traditionally set using servlet parameters. Refer to the documentation of Hystrix for more details.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.hystrix.stream.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the hystrix.stream endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.pause.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the /pause endpoint (to send Lifecycle.stop()).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.refresh.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the /refresh endpoint to refresh configuration and re-initialize refresh scoped beans.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.restart.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the /restart endpoint to restart the application context.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.resume.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the /resume endpoint (to send Lifecycle.start()).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.service-registry.cache.time-to-live</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum time that a response can be cached.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.endpoint.service-registry.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable the service-registry endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.health.refresh.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the health endpoint for the refresh scope.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.health.zookeeper.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the health endpoint for zookeeper.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management.metrics.binders.hystrix.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables creation of OK Http Client factory beans.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.checksum-policy</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.connect-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.local-repository</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.offline</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.proxy</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.remote-repositories</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.request-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.resolve-pom</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>maven.update-policy</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>proxy.auth.load-balanced</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>proxy.auth.routes</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Authentication strategy per route.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>ribbon.eager-load.clients</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>ribbon.eager-load.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>ribbon.http.client.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Deprecated property to enable Ribbon RestClient.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>ribbon.okhttp.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables the use of the OK HTTP Client with Ribbon.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>ribbon.restclient.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables the use of the deprecated Ribbon RestClient.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>ribbon.secure-ports</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.bus.ack.destination-service</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Service that wants to listen to acks. By default null (meaning all services).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.bus.ack.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to switch off acks (default on).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.bus.destination</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>springCloudBus</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of Spring Cloud Stream destination for messages.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.bus.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that the bus is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.bus.env.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to switch off environment change events (default on).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.bus.id</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The identifier for this application instance.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.bus.refresh.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to switch off refresh events (default on).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.bus.trace.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to switch on tracing of acks (default off).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.discovery.default-server-port</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>80</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Port to use when no port is defined by ribbon.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.discovery.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that discovery is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.discovery.heartbeat-frequency</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>5000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Frequency in milliseconds of poll for heart beat. The client will poll on this frequency and broadcast a list of service ids.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.discovery.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Order of the discovery client used by <code class="literal">CompositeDiscoveryClient</code> for sorting available clients.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.org</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Organization name to initially target.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password for user to authenticate and obtain token.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.skip-ssl-validation</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.space</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Space name to initially target.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.url</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>URL of Cloud Foundry API (Cloud Controller).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.cloudfoundry.username</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Username to authenticate (usually an email address).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.compatibility-verifier.compatible-boot-versions</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2.1.x</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default accepted versions for the Spring Boot dependency. You can set {@code x} for the patch version if you don&#8217;t want to specify a concrete value. Example: {@code 3.4.x}</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.compatibility-verifier.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables creation of Spring Cloud compatibility verification.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.allow-override</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that {@link #isOverrideSystemProperties() systemPropertiesOverride} can be used. Set to false to prevent users from changing the default accidentally. Default true.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.discovery.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that config server discovery is enabled (config server URL will be looked up via discovery).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.discovery.service-id</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>configserver</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Service id to locate config server.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to say that remote configuration is enabled. Default true;</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.fail-fast</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that failure to connect to the server is fatal (default false).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.headers</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Additional headers used to create the client request.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.label</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The label name to use to pull remote configuration properties. The default is set on the server (generally "master" for a git based server).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of application used to fetch remote properties.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.override-none</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that when {@link #setAllowOverride(boolean) allowOverride} is true, external properties should take lowest priority and should not override any existing property sources (including local config files). Default false.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.override-system-properties</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that the external properties should override system properties. Default true.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The password to use (HTTP Basic) when contacting the remote server.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.profile</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>default</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The default profile to use when fetching remote configuration (comma-separated). Default is "default".</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.request-read-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>timeout on waiting to read data from the Config Server.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.retry.initial-interval</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Initial retry interval in milliseconds.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.retry.max-attempts</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>6</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum number of attempts.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.retry.max-interval</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum interval for backoff.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.retry.multiplier</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1.1</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Multiplier for next interval.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.send-state</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate whether to send state. Default true.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.accept-empty</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that If HTTP 404 needs to be sent if Application is not Found</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.bootstrap</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag indicating that the config server should initialize its own Environment with properties from the remote repository. Off by default because it delays startup but can be useful when embedding the server in another application.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.default-application-name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default application name when incoming requests do not have a specific one.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.default-label</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default repository label when incoming requests do not have a specific label.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.default-profile</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>default</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default application profile when incoming requests do not have a specific one.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.encrypt.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable decryption of environment properties before sending to client.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.basedir</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Base directory for local working copy of repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.clone-on-start</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that the repository should be cloned on startup (not on demand). Generally leads to slower startup but faster first query.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.default-label</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The default label to be used with the remore repository</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.delete-untracked-branches</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that the branch should be deleted locally if it&#8217;s origin tracked branch was removed.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.force-pull</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that the repository should force pull. If true discard any local changes and take from remote repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.host-key</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Valid SSH host key. Must be set if hostKeyAlgorithm is also set.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.host-key-algorithm</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>One of ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp521. Must be set if hostKey is also set.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.ignore-local-ssh-settings</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If true, use property-based instead of file-based SSH config.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.known-hosts-file</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Location of custom .known_hosts file.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The order of the environment repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.passphrase</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Passphrase for unlocking your ssh private key.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password for authentication with remote repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.preferred-authentications</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Override server authentication method order. This should allow for evading login prompts if server has keyboard-interactive authentication before the publickey method.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.private-key</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Valid SSH private key. Must be set if ignoreLocalSshSettings is true and Git URI is SSH format.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.proxy</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>HTTP proxy configuration.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.refresh-rate</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Time (in seconds) between refresh of the git repository</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.repos</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Map of repository identifier to location and other properties.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.search-paths</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Search paths to use within local working copy. By default searches only the root.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.skip-ssl-validation</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that SSL certificate validation should be bypassed when communicating with a repository served over an HTTPS connection.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.strict-host-key-checking</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If false, ignore errors with host key</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>5</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Timeout (in seconds) for obtaining HTTP or SSH connection (if applicable), defaults to 5 seconds.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.uri</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>URI of remote repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.git.username</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Username for authentication with remote repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.health.repositories</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.jdbc.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.jdbc.sql</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>SELECT KEY, VALUE from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>SQL used to query database for keys and values</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.native.add-label-locations</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to determine whether label locations should be added.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.native.default-label</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>master</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.native.fail-on-error</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to determine how to handle exceptions during decryption (default false).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.native.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.native.search-locations</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[]</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Locations to search for configuration files. Defaults to the same as a Spring Boot app so [classpath:/,classpath:/config/,file:./,file:./config/].</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.native.version</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Version string to be reported for native repository</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.overrides</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Extra map for a property source to be sent to all clients unconditionally.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.prefix</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Prefix for configuration resource paths (default is empty). Useful when embedding in another application when you don&#8217;t want to change the context path or servlet path.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.strip-document-from-yaml</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that YAML documents that are text or collections (not a map) should be returned in "native" form.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.basedir</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Base directory for local working copy of repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.default-label</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The default label to be used with the remore repository</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The order of the environment repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.passphrase</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Passphrase for unlocking your ssh private key.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password for authentication with remote repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.search-paths</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Search paths to use within local working copy. By default searches only the root.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.strict-host-key-checking</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Reject incoming SSH host keys from remote servers not in the known host list.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.uri</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>URI of remote repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.svn.username</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Username for authentication with remote repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.backend</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>secret</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Vault backend. Defaults to secret.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.default-key</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The key in vault shared by all applications. Defaults to application. Set to empty to disable.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.host</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>127.0.0.1</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Vault host. Defaults to 127.0.0.1.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.kv-version</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Value to indicate which version of Vault kv backend is used. Defaults to 1.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.port</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>8200</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Vault port. Defaults to 8200.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.profile-separator</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>,</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Vault profile separator. Defaults to comma.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.proxy</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>HTTP proxy configuration.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.scheme</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>http</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Vault scheme. Defaults to http.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.skip-ssl-validation</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that SSL certificate validation should be bypassed when communicating with a repository served over an HTTPS connection.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.server.vault.timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>5</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Timeout (in seconds) for obtaining HTTP connection, defaults to 5 seconds.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.token</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Security Token passed thru to underlying environment repository.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.uri</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[<a class="link" href="http://localhost:8888" target="_top">http://localhost:8888</a>]</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The URI of the remote server (default <a class="link" href="http://localhost:8888" target="_top">http://localhost:8888</a>).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.config.username</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The username to use (HTTP Basic) when contacting the remote server.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.acl-token</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.data-key</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>data</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If format is Format.PROPERTIES or Format.YAML then the following field is used as key to look up consul for configuration.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.default-context</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.fail-fast</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Throw exceptions during config lookup if true, otherwise, log warnings.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.format</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Alternative to spring.application.name to use in looking up values in consul KV.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.prefix</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>config</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.profile-separator</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>,</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.watch.delay</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The value of the fixed delay for the watch in millis. Defaults to 1000.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.watch.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If the watch is enabled. Defaults to true.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.config.watch.wait-time</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>55</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The number of seconds to wait (or block) for watch query, defaults to 55. Needs to be less than default ConsulClient (defaults to 60). To increase ConsulClient timeout create a ConsulClient bean with a custom ConsulRawClient with a custom HttpClient.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.acl-token</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.catalog-services-watch-delay</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The delay between calls to watch consul catalog in millis, default is 1000.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.catalog-services-watch-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The number of seconds to block while watching consul catalog, default is 2.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.datacenters</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Map of serviceId&#8217;s &#8594; datacenter to query for in server list. This allows looking up services in another datacenters.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.default-query-tag</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Tag to query for in service list if one is not listed in serverListQueryTags.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.default-zone-metadata-name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>zone</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Service instance zone comes from metadata. This allows changing the metadata tag name.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.deregister</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Disable automatic de-registration of service in consul.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Is service discovery enabled?</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.fail-fast</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Throw exceptions during service registration if true, otherwise, log warnings (defaults to true).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.health-check-critical-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Timeout to deregister services critical for longer than timeout (e.g. 30m). Requires consul version 7.x or higher.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.health-check-headers</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Headers to be applied to the Health Check calls</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.health-check-interval</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10s</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>How often to perform the health check (e.g. 10s), defaults to 10s.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.health-check-path</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/actuator/health</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Alternate server path to invoke for health checking</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.health-check-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Timeout for health check (e.g. 10s).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.health-check-tls-skip-verify</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Skips certificate verification during service checks if true, otherwise runs certificate verification.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.health-check-url</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Custom health check url to override default</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.heartbeat.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.heartbeat.interval-ratio</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.heartbeat.ttl-unit</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>s</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.heartbeat.ttl-value</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>30</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.hostname</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Hostname to use when accessing server</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.instance-group</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Service instance group</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.instance-id</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Unique service instance id</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.instance-zone</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Service instance zone</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.ip-address</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>IP address to use when accessing service (must also set preferIpAddress to use)</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.lifecycle.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.management-port</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Port to register the management service under (defaults to management port)</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.management-suffix</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>management</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Suffix to use when registering management service</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.management-tags</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Tags to use when registering management service</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Order of the discovery client used by <code class="literal">CompositeDiscoveryClient</code> for sorting available clients.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.port</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Port to register the service under (defaults to listening port)</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.prefer-agent-address</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Source of how we will determine the address to use</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.prefer-ip-address</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Use ip address rather than hostname during registration</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.query-passing</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Add the 'passing` parameter to /v1/health/service/serviceName. This pushes health check passing to the server.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.register</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Register as a service in consul.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.register-health-check</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Register health check in consul. Useful during development of a service.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.scheme</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>http</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to register an http or https service</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.server-list-query-tags</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Map of serviceId&#8217;s &#8594; tag to query for in server list. This allows filtering services by a single tag.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.service-name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Service name</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.discovery.tags</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Tags to use when registering service</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Is spring cloud consul enabled</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.host</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>localhost</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Consul agent hostname. Defaults to 'localhost'.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.port</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>8500</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Consul agent port. Defaults to '8500'.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.retry.initial-interval</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Initial retry interval in milliseconds.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.retry.max-attempts</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>6</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum number of attempts.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.retry.max-interval</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum interval for backoff.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.retry.multiplier</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1.1</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Multiplier for next interval.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.scheme</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Consul agent scheme (HTTP/HTTPS). If there is no scheme in address - client will use HTTP.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.tls.certificate-password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password to open the certificate.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.tls.certificate-path</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>File path to the certificate.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.tls.key-store-instance-type</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Type of key framework to use.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.tls.key-store-password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password to an external keystore</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.tls.key-store-path</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Path to an external keystore</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.cloudfoundry.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.composite-indicator.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables discovery client composite health indicator.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.health-indicator.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.health-indicator.include-description</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.simple.instances</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.simple.local.instance-id</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The unique identifier or name for the service instance.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.simple.local.metadata</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Metadata for the service instance. Can be used by discovery clients to modify their behaviour per instance, e.g. when load balancing.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.simple.local.service-id</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The identifier or name for the service. Multiple instances might share the same service ID.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.simple.local.uri</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The URI of the service instance. Will be parsed to extract the scheme, host, and port.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.client.simple.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.discovery.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables discovery client health indicators.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.features.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables the features endpoint.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.compile</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Configuration for function bodies, which will be compiled. The key in the map is the function name and the value is a map containing a key "lambda" which is the body to compile, and optionally a "type" (defaults to "function"). Can also contain "inputType" and "outputType" in case it is ambiguous.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.imports</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Configuration for a set of files containing function bodies, which will be imported and compiled. The key in the map is the function name and the value is another map, containing a "location" of the file to compile and (optionally) a "type" (defaults to "function").</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.task.consumer</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.task.function</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.task.supplier</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.web.path</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Path to web resources for functions (should start with / if not empty).</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.web.supplier.auto-startup</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.web.supplier.debug</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.web.supplier.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.web.supplier.headers</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.web.supplier.name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.function.web.supplier.template-url</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.default-filters</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List of filter definitions that are applied to every route.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.discovery.locator.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag that enables DiscoveryClient gateway integration</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.discovery.locator.filters</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.discovery.locator.include-expression</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>SpEL expression that will evaluate whether to include a service in gateway integration or not, defaults to: true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.discovery.locator.lower-case-service-id</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Option to lower case serviceId in predicates and filters, defaults to false. Useful with eureka when it automatically uppercases serviceId. so MYSERIVCE, would match /myservice/**</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.discovery.locator.predicates</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.discovery.locator.route-id-prefix</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The prefix for the routeId, defaults to discoveryClient.getClass().getSimpleName() + "_". Service Id will be appended to create the routeId.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.discovery.locator.url-expression</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>'lb://'+serviceId</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>SpEL expression that create the uri for each route, defaults to: 'lb://'+serviceId</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables gateway functionality.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.remove-hop-by-hop.headers</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.remove-hop-by-hop.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.secure-headers.content-security-policy</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>default-src 'self' https:; font-src 'self' https: data:; img-src 'self' https: data:; object-src 'none'; script-src https:; style-src 'self' https: 'unsafe-inline'</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.secure-headers.content-type-options</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>nosniff</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.secure-headers.download-options</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>noopen</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.secure-headers.frame-options</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>DENY</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.secure-headers.permitted-cross-domain-policies</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>none</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.secure-headers.referrer-policy</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>no-referrer</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.secure-headers.strict-transport-security</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>max-age=631138519</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.filter.secure-headers.xss-protection-header</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1 ; mode=block</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.forwarded.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables the ForwardedHeadersFilter.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.globalcors.cors-configurations</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.connect-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The connect timeout in millis, the default is 45s.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.pool.acquire-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Only for type FIXED, the maximum time in millis to wait for aquiring.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.pool.max-connections</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Only for type FIXED, the maximum number of connections before starting pending acquisition on existing ones.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.pool.name</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>proxy</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The channel pool map name, defaults to proxy.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.pool.type</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Type of pool for HttpClient to use, defaults to ELASTIC.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.proxy.host</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Hostname for proxy configuration of Netty HttpClient.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.proxy.non-proxy-hosts-pattern</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Regular expression (Java) for a configured list of hosts that should be reached directly, bypassing the proxy</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.proxy.password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password for proxy configuration of Netty HttpClient.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.proxy.port</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Port for proxy configuration of Netty HttpClient.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.proxy.username</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Username for proxy configuration of Netty HttpClient.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.response-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The response timeout.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.close-notify-flush-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>3000ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>SSL close_notify flush timeout. Default to 3000 ms.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.close-notify-flush-timeout-millis</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.close-notify-read-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>SSL close_notify read timeout. Default to 0 ms.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.close-notify-read-timeout-millis</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.default-configuration-type</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The default ssl configuration type. Defaults to TCP.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.handshake-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10000ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>SSL handshake timeout. Default to 10000 ms</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.handshake-timeout-millis</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.trusted-x509-certificates</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Trusted certificates for verifying the remote endpoint&#8217;s certificate.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.httpclient.ssl.use-insecure-trust-manager</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Installs the netty InsecureTrustManagerFactory. This is insecure and not suitable for production.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.metrics.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables the collection of metrics data.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.proxy.headers</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Fixed header values that will be added to all downstream requests.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.proxy.sensitive</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>A set of sensitive header names that will not be sent downstream by default.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.redis-rate-limiter.burst-capacity-header</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>X-RateLimit-Burst-Capacity</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The name of the header that returns the burst capacity configuration.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.redis-rate-limiter.config</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.redis-rate-limiter.include-headers</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether or not to include headers containing rate limiter information, defaults to true.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.redis-rate-limiter.remaining-header</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>X-RateLimit-Remaining</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The name of the header that returns number of remaining requests during the current second.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.redis-rate-limiter.replenish-rate-header</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>X-RateLimit-Replenish-Rate</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The name of the header that returns the replenish rate configuration.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.routes</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List of Routes</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.streaming-media-types</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If the XForwardedHeadersFilter is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.for-append</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If appending X-Forwarded-For as a list is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.for-enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If X-Forwarded-For is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.host-append</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If appending X-Forwarded-Host as a list is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.host-enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If X-Forwarded-Host is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The order of the XForwardedHeadersFilter.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.port-append</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If appending X-Forwarded-Port as a list is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.port-enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If X-Forwarded-Port is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.prefix-append</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If appending X-Forwarded-Prefix as a list is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.prefix-enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If X-Forwarded-Prefix is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.proto-append</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If appending X-Forwarded-Proto as a list is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.gateway.x-forwarded.proto-enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If X-Forwarded-Proto is enabled.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.httpclientfactories.apache.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables creation of Apache Http Client factory beans.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.httpclientfactories.ok.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables creation of OK Http Client factory beans.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.hypermedia.refresh.fixed-delay</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>5000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.hypermedia.refresh.initial-delay</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10000</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.inetutils.default-hostname</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>localhost</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The default hostname. Used in case of errors.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.inetutils.default-ip-address</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>127.0.0.1</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The default IP address. Used in case of errors.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.inetutils.ignored-interfaces</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List of Java regular expressions for network interfaces that will be ignored.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.inetutils.preferred-networks</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List of Java regular expressions for network addresses that will be preferred.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.inetutils.timeout-seconds</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Timeout, in seconds, for calculating hostname.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.inetutils.use-only-site-local-interfaces</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to use only interfaces with site local addresses. See {@link InetAddress#isSiteLocalAddress()} for more details.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.loadbalancer.retry.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.refresh.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables autoconfiguration for the refresh scope and associated features.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.refresh.extra-refreshable</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Additional class names for beans to post process into refresh scope.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.service-registry.auto-registration.enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether service auto-registration is enabled. Defaults to true.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.service-registry.auto-registration.fail-fast</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether startup fails if there is no AutoServiceRegistration. Defaults to false.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.service-registry.auto-registration.register-management</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to register the management as a service. Defaults to true.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.binders</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Additional per-binder properties (see {@link BinderProperties}) if more then one binder of the same type is used (i.e., connect to multiple instances of RabbitMq). Here you can specify multiple binder configurations, each with different environment settings. For example; spring.cloud.stream.binders.rabbit1.environment. . . , spring.cloud.stream.binders.rabbit2.environment. . .</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.binding-retry-interval</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>30</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Retry interval (in seconds) used to schedule binding attempts. Default: 30 sec.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.bindings</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Additional binding properties (see {@link BinderProperties}) per binding name (e.g., 'input`). For example; This sets the content-type for the 'input' binding of a Sink application: 'spring.cloud.stream.bindings.input.contentType=text/plain'</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.consul.binder.event-timeout</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>5</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.default-binder</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The name of the binder to use by all bindings in the event multiple binders available (e.g., 'rabbit');</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.dynamic-destinations</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[]</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>A list of destinations that can be bound dynamically. If set, only listed destinations can be bound.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.function.definition</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Definition of functions to bind. If several functions need to be composed into one, use pipes (e.g., 'fooFunc</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>barFunc')</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.instance-count</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The number of deployed instances of an application. Default: 1. NOTE: Could also be managed per individual binding "spring.cloud.stream.bindings.foo.consumer.instance-count" where 'foo' is the name of the binding.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.instance-index</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The instance id of the application: a number from 0 to instanceCount-1. Used for partitioning and with Kafka. NOTE: Could also be managed per individual binding "spring.cloud.stream.bindings.foo.consumer.instance-index" where 'foo' is the name of the binding.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.integration.message-handler-not-propagated-headers</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Message header names that will NOT be copied from the inbound message.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.auto-add-partitions</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.auto-create-topics</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.brokers</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[localhost]</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.configuration</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Arbitrary kafka properties that apply to both producers and consumers.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.consumer-properties</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Arbitrary kafka consumer properties.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.fetch-size</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.header-mapper-bean-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The bean name of a custom header mapper to use instead of a {@link org.springframework.kafka.support.DefaultKafkaHeaderMapper}.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.headers</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[]</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.health-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>60</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Time to wait to get partition information in seconds; default 60.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.jaas</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.max-wait</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>100</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.min-partition-count</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.offset-update-count</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.offset-update-shutdown-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.offset-update-time-window</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.producer-properties</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Arbitrary kafka producer properties.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.queue-size</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>8192</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.replication-factor</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.required-acks</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.socket-buffer-size</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2097152</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.admin</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.batch-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.buffer-size</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.compression-type</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.configuration</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.error-channel-enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.header-mode</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.header-patterns</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.message-key-expression</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.partition-count</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.partition-key-expression</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.partition-key-extractor-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.partition-selector-expression</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.partition-selector-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.required-groups</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.sync</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.producer.use-native-encoding</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.transaction.transaction-id-prefix</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.zk-connection-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>ZK Connection timeout in milliseconds.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.zk-nodes</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[localhost]</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.binder.zk-session-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>ZK session timeout in milliseconds.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.bindings</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.application-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.auto-add-partitions</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.auto-create-topics</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.brokers</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.configuration</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.consumer-properties</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.fetch-size</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.header-mapper-bean-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.headers</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.health-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.jaas</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.max-wait</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.min-partition-count</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.offset-update-count</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.offset-update-shutdown-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.offset-update-time-window</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.producer-properties</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.queue-size</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.replication-factor</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.required-acks</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.serde-error</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>{@link org.apache.kafka.streams.errors.DeserializationExceptionHandler} to use when there is a Serde error. {@link KafkaStreamsBinderConfigurationProperties.SerdeError} values are used to provide the exception handler on consumer binding.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.socket-buffer-size</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.zk-connection-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.zk-nodes</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.binder.zk-session-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.bindings</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.time-window.advance-by</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.kafka.streams.time-window.length</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.metrics.export-properties</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List of properties that are going to be appended to each message. This gets populate by onApplicationEvent, once the context refreshes to avoid overhead of doing per message basis.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.metrics.key</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The name of the metric being emitted. Should be an unique value per application. Defaults to: ${spring.application.name:${vcap.application.name:${spring.config.name:application}}}</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.metrics.meter-filter</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pattern to control the 'meters' one wants to capture. By default all 'meters' will be captured. For example, 'spring.integration.*' will only capture metric information for meters whose name starts with 'spring.integration'.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.metrics.properties</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Application properties that should be added to the metrics payload For example: <code class="literal">spring.application**</code></p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.metrics.schedule-interval</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>60s</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Interval expressed as Duration for scheduling metrics snapshots publishing. Defaults to 60 seconds</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.override-cloud-connectors</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is only applicable when the cloud profile is active and Spring Cloud Connectors are provided with the application. If the property is false (the default), the binder detects a suitable bound service (for example, a RabbitMQ service bound in Cloud Foundry for the RabbitMQ binder) and uses it for creating connections (usually through Spring Cloud Connectors). When set to true, this property instructs binders to completely ignore the bound services and rely on Spring Boot properties (for example, relying on the spring.rabbitmq.* properties provided in the environment for the RabbitMQ binder). The typical usage of this property is to be nested in a customized environment when connecting to multiple systems.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.rabbit.binder.admin-addresses</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[]</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Urls for management plugins; only needed for queue affinity.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.rabbit.binder.admin-adresses</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.rabbit.binder.compression-level</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Compression level for compressed bindings; see 'java.util.zip.Deflator'.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.rabbit.binder.connection-name-prefix</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Prefix for connection names from this binder.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.rabbit.binder.nodes</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[]</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Cluster member node names; only needed for queue affinity.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.rabbit.bindings</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema-registry-client.cached</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema-registry-client.endpoint</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema.avro.dynamic-schema-generation-enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema.avro.prefix</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>vnd</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema.avro.reader-schema</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema.avro.schema-imports</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>A list of files or directories that should be loaded first thus making them importable by subsequent schemas. Note that imported files should not reference each other. @parameter</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema.avro.schema-locations</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The source directory of Apache Avro schema. This schema is used by this converter. If this schema depends on other schemas consider defining those those dependent ones in the {@link #schemaImports} @parameter</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema.server.allow-schema-deletion</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Boolean flag to enable/disable schema deletion.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.stream.schema.server.path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Prefix for configuration resource paths (default is empty). Useful when embedding in another application when you don&#8217;t want to change the context path or servlet path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.command-line-runner-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The order for the {@code CommandLineRunner} used to run batch jobs when {@code spring.cloud.task.batch.fail-on-job-failure=true}. Defaults to 0 (same as the {@link org.springframework.boot.autoconfigure.batch.JobLauncherCommandLineRunner}).</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.chunk-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Properties for chunk listener order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.chunk.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task should listen for batch chunk events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task should listen for batch events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.item-process-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Properties for itemProcess listener order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.item-process.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task should listen for batch item processed events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.item-read-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Properties for itemRead listener order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.item-read.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task should listen for batch item read events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.item-write-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Properties for itemWrite listener order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.item-write.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task should listen for batch item write events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.job-execution-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Properties for jobExecution listener order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.job-execution.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task should listen for batch job execution events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.skip-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Properties for skip listener order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.skip.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task should listen for batch skip events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.step-execution-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Properties for stepExecution listener order</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.events.step-execution.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task should listen for batch step execution events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.fail-on-job-failure</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task app should return with a non zero exit code if a batch job fails.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.fail-on-job-failure-poll-interval</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>5000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Fixed delay in milliseconds that Spring Cloud Task will wait when checking if {@link org.springframework.batch.core.JobExecution}s have completed, when spring.cloud.task.batch.failOnJobFailure is set to true. Defaults to 5000.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.fail-on-job-failurewait-time</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum wait time in milliseconds that Spring Cloud Task will wait for tasks to complete when spring.cloud.task.batch.failOnJobFailure is set to true. Defaults to 0. 0 indicates no wait time is enforced.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.job-names</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Comma-separated list of job names to execute on startup (for instance, <code class="literal">job1,job2</code>). By default, all Jobs found in the context are executed.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.batch.listener.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task will be linked to the batch jobs that are run.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.closecontext-enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>When set to true the context is closed at the end of the task. Else the context remains open.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.events.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task app should emit task events.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.executionid</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>An id that will be used by the task when updating the task execution.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.external-execution-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>An id that can be associated with a task.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.parent-execution-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The id of the parent task execution id that launched this task execution. Defaults to null if task execution had no parent.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.single-instance-enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>This property is used to determine if a task will execute if another task with the same app name is running.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.single-instance-lock-check-interval</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>500</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Declares the time (in millis) that a task execution will wait between checks. Default time is: 500 millis.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.single-instance-lock-ttl</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Declares the maximum amount of time (in millis) that a task execution can hold a lock to prevent another task from executing with a specific task name when the single-instance-enabled is set to true. Default time is: Integer.MAX_VALUE.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.task.table-prefix</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>TASK_</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The prefix to append to the table names created by Spring Cloud Task.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.util.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables creation of Spring Cloud utility beans.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.app-id.app-id-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>app-id</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the AppId authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.app-id.network-interface</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Network interface hint for the "MAC_ADDRESS" UserId mechanism.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.app-id.user-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>MAC_ADDRESS</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>UserId mechanism. Can be either "MAC_ADDRESS", "IP_ADDRESS", a string or a class name.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.app-role.app-role-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>approle</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the AppRole authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.app-role.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the role, optional, used for pull-mode.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.app-role.role-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The RoleId.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.app-role.secret-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The SecretId.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.application-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Application name for AppId authentication.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.authentication</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws-ec2.aws-ec2-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws-ec2</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the AWS-EC2 authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws-ec2.identity-document</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><a class="link" href="http://169.254.169.254/latest/dynamic/instance-identity/pkcs7" target="_top">http://169.254.169.254/latest/dynamic/instance-identity/pkcs7</a></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>URL of the AWS-EC2 PKCS7 identity document.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws-ec2.nonce</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Nonce used for AWS-EC2 authentication. An empty nonce defaults to nonce generation.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws-ec2.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the role, optional.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws-iam.aws-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the AWS authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws-iam.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the role, optional. Defaults to the friendly IAM name if not set.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws-iam.server-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the server used to set {@code X-Vault-AWS-IAM-Server-ID} header in the headers of login requests.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws.access-key-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>cloud.aws.credentials.accessKey</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained access key.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>aws backend path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable aws backend usage.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Role name for credentials.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.aws.secret-key-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>cloud.aws.credentials.secretKey</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained secret key.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.azure-msi.azure-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>azure</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the Azure MSI authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.azure-msi.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the role.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.cassandra.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>cassandra</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Cassandra backend path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.cassandra.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable cassandra backend usage.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.cassandra.password-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.data.cassandra.password</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained password.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.cassandra.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Role name for credentials.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.cassandra.username-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.data.cassandra.username</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained username.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.config.lifecycle.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable lifecycle management.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.config.order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Used to set a {@link org.springframework.core.env.PropertySource} priority. This is useful to use Vault as an override on other property sources. @see org.springframework.core.PriorityOrdered</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.connection-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>5000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Connection timeout;</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.consul.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>consul</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Consul backend path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.consul.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable consul backend usage.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.consul.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Role name for credentials.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.consul.token-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.consul.token</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained token.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.database.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>database</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Database backend path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.database.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable database backend usage.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.database.password-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.datasource.password</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained password.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.database.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Role name for credentials.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.database.username-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.datasource.username</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained username.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.discovery.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to indicate that Vault server discovery is enabled (vault server URL will be looked up via discovery).</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.discovery.service-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>vault</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Service id to locate Vault.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable Vault config server.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.fail-fast</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Fail fast if data cannot be obtained from Vault.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-gce.gcp-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>gcp</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the Kubernetes authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-gce.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the role against which the login is being attempted.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-gce.service-account</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Optional service account id. Using the default id if left unconfigured.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-iam.credentials.encoded-key</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The base64 encoded contents of an OAuth2 account private key in JSON format.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-iam.credentials.location</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Location of the OAuth2 credentials private key. &lt;p&gt; Since this is a Resource, the private key can be in a multitude of locations, such as a local file system, classpath, URL, etc.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-iam.gcp-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>gcp</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the Kubernetes authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-iam.jwt-validity</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>15m</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Validity of the JWT token.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-iam.project-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Overrides the GCP project Id.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-iam.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the role against which the login is being attempted.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.gcp-iam.service-account-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Overrides the GCP service account Id.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.generic.application-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Application name to be used for the context.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.generic.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>secret</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the default backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.generic.default-context</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the default context.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.generic.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the generic backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.generic.profile-separator</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Profile-separator to combine application name and profile.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.host</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>localhost</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Vault server host.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kubernetes.kubernetes-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>kubernetes</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the Kubernetes authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kubernetes.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the role against which the login is being attempted.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kubernetes.service-account-token-file</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/var/run/secrets/kubernetes.io/serviceaccount/token</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Path to the service account token file.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kv.application-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Application name to be used for the context.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kv.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>secret</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the default backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kv.backend-version</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>2</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Key-Value backend version. Currently supported versions are: &lt;ul&gt; &lt;li&gt;Version 1 (unversioned key-value backend).&lt;/li&gt; &lt;li&gt;Version 2 (versioned key-value backend).&lt;/li&gt; &lt;/ul&gt;</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kv.default-context</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>application</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Name of the default context.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kv.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable the kev-value backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.kv.profile-separator</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Profile-separator to combine application name and profile.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mongodb.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>mongodb</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Cassandra backend path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mongodb.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable mongodb backend usage.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mongodb.password-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.data.mongodb.password</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained password.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mongodb.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Role name for credentials.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mongodb.username-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.data.mongodb.username</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained username.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mysql.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>mysql</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>mysql backend path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mysql.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable mysql backend usage.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mysql.password-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.datasource.password</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained username.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mysql.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Role name for credentials.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.mysql.username-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.datasource.username</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained username.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>8200</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Vault server port.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.postgresql.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>postgresql</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>postgresql backend path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.postgresql.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable postgresql backend usage.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.postgresql.password-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.datasource.password</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained username.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.postgresql.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Role name for credentials.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.postgresql.username-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.datasource.username</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained username.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.rabbitmq.backend</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>rabbitmq</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>rabbitmq backend path.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.rabbitmq.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable rabbitmq backend usage.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.rabbitmq.password-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.rabbitmq.password</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained password.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.rabbitmq.role</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Role name for credentials.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.rabbitmq.username-property</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.rabbitmq.username</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Target property for the obtained username.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.read-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>15000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Read timeout;</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.scheme</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>https</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Protocol scheme. Can be either "http" or "https".</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.ssl.cert-auth-path</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>cert</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mount path of the TLS cert authentication backend.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.ssl.key-store</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Trust store that holds certificates and private keys.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.ssl.key-store-password</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password used to access the key store.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.ssl.trust-store</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Trust store that holds SSL certificates.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.ssl.trust-store-password</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Password used to access the trust store.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.token</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Static vault token. Required if {@link #authentication} is {@code TOKEN}.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.vault.uri</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Vault URI. Can be set with scheme, host and port.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.base-sleep-time-ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>50</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Initial amount of time to wait between retries</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.block-until-connected-unit</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The unit of time related to blocking on connection to Zookeeper</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.block-until-connected-wait</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Wait time to block on connection to Zookeeper</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.connect-string</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>localhost:2181</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Connection string to the Zookeeper cluster</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.default-health-endpoint</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Default health endpoint that will be checked to verify that a dependency is alive</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.dependencies</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mapping of alias to ZookeeperDependency. From Ribbon perspective the alias is actually serviceID since Ribbon can&#8217;t accept nested structures in serviceID</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.dependency-configurations</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.dependency-names</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.initial-status</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The initial status of this instance (defaults to {@link StatusConstants#STATUS_UP}).</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.instance-host</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Predefined host with which a service can register itself in Zookeeper. Corresponds to the {code address} from the URI spec.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.instance-id</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Id used to register with zookeeper. Defaults to a random UUID.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.instance-port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Port to register the service under (defaults to listening port)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.instance-ssl-port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Ssl port of the registered service.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.metadata</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Gets the metadata name/value pairs associated with this instance. This information is sent to zookeeper and can be used by other instances.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Order of the discovery client used by <code class="literal">CompositeDiscoveryClient</code> for sorting available clients.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.register</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Register as a service in zookeeper.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.root</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/services</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Root Zookeeper folder in which all instances are registered</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.discovery.uri-spec</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>{scheme}://{address}:{port}</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The URI specification to resolve during service registration in Zookeeper</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Is Zookeeper enabled</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.max-retries</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Max number of times to retry</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.max-sleep-ms</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>500</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Max time in ms to sleep on each retry</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.cloud.zookeeper.prefix</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Common prefix that will be applied to all Zookeeper dependencies' paths</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.integration.poller.fixed-delay</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Fixed delay for default poller.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.integration.poller.max-messages-per-poll</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Maximum messages per poll for the default poller.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.annotation.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.async.configurer.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable default AsyncConfigurer.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.async.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable instrumenting async related components so that the tracing information is passed between threads.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.async.ignored-beans</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List of {@link java.util.concurrent.Executor} bean names that should be ignored and not wrapped in a trace representation.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.baggage-keys</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List of baggage key names that should be propagated out of process. These keys will be prefixed with <code class="literal">baggage</code> before the actual key. This property is set in order to be backward compatible with previous Sleuth versions. @see brave.propagation.ExtraFieldPropagation.FactoryBuilder#addPrefixedFields(String, java.util.Collection)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.feign.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable span information propagation when using Feign.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.feign.processor.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable post processor that wraps Feign Context in its tracing representations.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.http.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.http.legacy.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.hystrix.strategy.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable custom HystrixConcurrencyStrategy that wraps all Callable instances into their Sleuth representative - the TraceCallable.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.integration.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable Spring Integration sleuth instrumentation.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.integration.patterns</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[!hystrixStreamOutput*, *]</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>An array of patterns against which channel names will be matched. @see org.springframework.integration.config.GlobalChannelInterceptor#patterns(). Defaults to any channel name not matching the Hystrix Stream channel name.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.integration.websockets.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable tracing for WebSockets.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.keys.http.headers</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Additional headers that should be added as tags if they exist. If the header value is multi-valued, the tag value will be a comma-separated, single-quoted list.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.keys.http.prefix</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>http.</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Prefix for header names if they are added as tags.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.log.slf4j.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable a {@link Slf4jScopeDecorator} that prints tracing information in the logs.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.log.slf4j.whitelisted-mdc-keys</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>A list of keys to be put from baggage to MDC.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.messaging.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Should messaging be turned on.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.messaging.jms.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.messaging.jms.remote-service-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>jms</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.messaging.kafka.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.messaging.kafka.remote-service-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>kafka</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.messaging.rabbit.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.messaging.rabbit.remote-service-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>rabbitmq</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.opentracing.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.propagation-keys</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>List of fields that are referenced the same in-process as it is on the wire. For example, the name "x-vcap-request-id" would be set as-is including the prefix. &lt;p&gt; Note: {@code fieldName} will be implicitly lower-cased. @see brave.propagation.ExtraFieldPropagation.FactoryBuilder#addField(String)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.reactor.enabled.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>When true enables instrumentation for reactor.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.rxjava.schedulers.hook.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable support for RxJava via RxJavaSchedulersHook.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.rxjava.schedulers.ignoredthreads</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[HystrixMetricPoller, ^RxComputation.*$]</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Thread names for which spans will not be sampled.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.sampler.probability</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>0.1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Probability of requests that should be sampled. E.g. 1.0 - 100% requests should be sampled. The precision is whole-numbers only (i.e. there&#8217;s no support for 0.1% of the traces).</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.scheduled.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable tracing for {@link org.springframework.scheduling.annotation.Scheduled}.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.scheduled.skip-pattern</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>org.springframework.cloud.netflix.hystrix.stream.HystrixStreamTask</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pattern for the fully qualified name of a class that should be skipped.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.supports-join</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>True means the tracing system supports sharing a span ID between a client and server.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.trace-id128</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>When true, generate 128-bit trace IDs instead of 64-bit ones.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.web.additional-skip-pattern</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Additional pattern for URLs that should be skipped in tracing. This will be appended to the {@link SleuthWebProperties#skipPattern}.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.web.client.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable interceptor injecting into {@link org.springframework.web.client.RestTemplate}.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.web.client.skip-pattern</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pattern for URLs that should be skipped in client side tracing.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.web.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>When true enables instrumentation for web applications.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.web.exception-throwing-filter-enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Flag to toggle the presence of a filter that logs thrown exceptions.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.web.filter-order</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Order in which the tracing filters should be registered. Defaults to {@link TraceHttpAutoConfiguration#TRACING_FILTER_ORDER}.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.web.skip-pattern</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/api-docs.*</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/autoconfig</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/configprops</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/dump</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/health</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/info</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/metrics.*</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/mappings</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/trace</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/swagger.*</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>.*\.png</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>.*\.css</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>.*\.js</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>.*\.html</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/favicon.ico</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/hystrix.stream</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/application/.*</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/actuator.*</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>/cloudfoundryapplication</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pattern for URLs that should be skipped in tracing.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.sleuth.zuul.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enable span information propagation when using Zuul.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.base-url</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p><a class="link" href="http://localhost:9411/" target="_top">http://localhost:9411/</a></p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>URL of the zipkin query server instance. You can also provide the service id of the Zipkin server if Zipkin&#8217;s registered in service discovery (e.g. <a class="link" href="http://zipkinserver/" target="_top">http://zipkinserver/</a>)</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.compression.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.discovery-client-enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If set to {@code false}, will treat the {@link ZipkinProperties#baseUrl} as a URL always</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enables sending spans to Zipkin</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.encoder</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Encoding type of spans sent to Zipkin. Set to {@link SpanBytesEncoder#JSON_V1} if your server is not recent.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.locator.discovery.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Enabling of locating the host name via service discovery</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.message-timeout</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Timeout in seconds before pending spans will be sent in batches to Zipkin</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.sender.type</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Means of sending spans to Zipkin</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>spring.zipkin.service.name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The name of the service, from which the Span was sent via HTTP, that should appear in Zipkin</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.amqp.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable support for Stub Runner and AMQP.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.amqp.mockCOnnection</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable support for Stub Runner and AMQP mocked connection factory.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.classifier</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubs</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The classifier to use by default in ivy co-ordinates for a stub.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.cloud.consul.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable stubs registration in Consul.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.cloud.delegate.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable DiscoveryClient&#8217;s Stub Runner implementation.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.cloud.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable Spring Cloud support for Stub Runner.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.cloud.eureka.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable stubs registration in Eureka.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.cloud.ribbon.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable Stub Runner&#8217;s Ribbon integration.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.cloud.stubbed.discovery.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether Service Discovery should be stubbed for Stub Runner. If set to false, stubs will get registered in real service discovery.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.cloud.zookeeper.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable stubs registration in Zookeeper.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.consumer-name</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>You can override the default {@code spring.application.name} of this field by setting a value to this parameter.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.delete-stubs-after-test</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>If set to {@code false} will NOT delete stubs from a temporary folder after running tests</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.ids</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>[]</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>The ids of the stubs to run in "ivy" notation ([groupId]:artifactId:[version]:[classifier][:port]). {@code groupId}, {@code classifier}, {@code version} and {@code port} can be optional.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.ids-to-service-ids</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Mapping of Ivy notation based ids to serviceIds inside your application Example "a:b" &#8594; "myService" "artifactId" &#8594; "myOtherService"</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.integration.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable Stub Runner integration with Spring Integration.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.mappings-output-folder</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Dumps the mappings of each HTTP server to the selected folder</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.max-port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>15000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Max value of a port for the automatically started WireMock server</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.min-port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>10000</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Min value of a port for the automatically started WireMock server</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.password</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Repository password</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.properties</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Map of properties that can be passed to custom {@link org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder}</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.proxy-host</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Repository proxy host</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.proxy-port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Repository proxy port</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.stream.enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>true</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Whether to enable Stub Runner integration with Spring Cloud Stream.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.stubs-mode</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Pick where the stubs should come from</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.stubs-per-consumer</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Should only stubs for this particular consumer get registered in HTTP server stub.</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>stubrunner.username</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>Repository username</p></td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>wiremock.rest-template-ssl-enabled</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>false</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>wiremock.server.files</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>wiremock.server.https-port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>-1</p></td></tr><tr><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; border-bottom: 0.5pt solid ; " align="left" valign="top"><p>wiremock.server.port</p></td><td style="border-bottom: 0.5pt solid ; " align="left" valign="top"><p>8080</p></td></tr><tr><td style="border-right: 0.5pt solid ; " align="left" valign="top">&nbsp;</td><td style="border-right: 0.5pt solid ; " align="left" valign="top"><p>wiremock.server.stubs</p></td><td style="" align="left" valign="top">&nbsp;</td></tr></tbody></table></div></div></div></div></body></html>