From c8c745284841e3f7bea4d03cac0fb3ee533c3fdb Mon Sep 17 00:00:00 2001 From: Marcin Grzejszczak Date: Tue, 5 Feb 2019 18:46:31 +0100 Subject: [PATCH] Added checkstyle --- .editorconfig | 14 + .settings.xml | 128 +- .springformat | 0 asciidoctor.css | 2404 ++++++++++++++--- docs/pom.xml | 5 +- pom.xml | 55 +- spring-cloud-openfeign-core/pom.xml | 27 +- .../AnnotatedParameterProcessor.java | 17 +- .../openfeign/DefaultFeignLoggerFactory.java | 2 +- .../cloud/openfeign/DefaultTargeter.java | 8 +- .../cloud/openfeign/EnableFeignClients.java | 9 +- .../openfeign/FeignAutoConfiguration.java | 70 +- .../cloud/openfeign/FeignClient.java | 32 +- .../cloud/openfeign/FeignClientBuilder.java | 13 +- .../openfeign/FeignClientFactoryBean.java | 178 +- .../openfeign/FeignClientProperties.java | 96 +- .../openfeign/FeignClientSpecification.java | 36 +- .../openfeign/FeignClientsConfiguration.java | 57 +- .../openfeign/FeignClientsRegistrar.java | 145 +- .../cloud/openfeign/FeignContext.java | 2 +- .../openfeign/FeignFormatterRegistrar.java | 2 +- .../cloud/openfeign/FeignLoggerFactory.java | 5 +- .../cloud/openfeign/HystrixTargeter.java | 53 +- .../cloud/openfeign/SpringQueryMap.java | 38 +- .../cloud/openfeign/Targeter.java | 9 +- .../PathVariableParameterProcessor.java | 13 +- .../QueryMapParameterProcessor.java | 15 +- .../RequestHeaderParameterProcessor.java | 16 +- .../RequestParamParameterProcessor.java | 16 +- .../encoding/BaseRequestInterceptor.java | 14 +- ...gnAcceptGzipEncodingAutoConfiguration.java | 10 +- .../FeignAcceptGzipEncodingInterceptor.java | 20 +- .../FeignClientEncodingProperties.java | 35 +- ...nContentGzipEncodingAutoConfiguration.java | 10 +- .../FeignContentGzipEncodingInterceptor.java | 27 +- .../openfeign/encoding/HttpEncoding.java | 3 +- .../CachingSpringLoadBalancerFactory.java | 25 +- ...DefaultFeignLoadBalancedConfiguration.java | 12 +- .../openfeign/ribbon/FeignLoadBalancer.java | 28 +- .../openfeign/ribbon/FeignRetryPolicy.java | 82 +- .../FeignRibbonClientAutoConfiguration.java | 22 +- ...pClientFeignLoadBalancedConfiguration.java | 74 +- .../ribbon/LoadBalancerFeignClient.java | 54 +- .../OkHttpFeignLoadBalancedConfiguration.java | 53 +- .../ribbon/RetryableFeignLoadBalancer.java | 99 +- .../RibbonResponseStatusCodeException.java | 17 +- .../openfeign/support/FallbackCommand.java | 34 +- .../support/FeignHttpClientProperties.java | 81 +- .../cloud/openfeign/support/FeignUtils.java | 8 +- .../support/ResponseEntityDecoder.java | 42 +- .../openfeign/support/SpringDecoder.java | 17 +- .../openfeign/support/SpringEncoder.java | 37 +- .../openfeign/support/SpringMvcContract.java | 148 +- ...itional-spring-configuration-metadata.json | 69 +- .../openfeign/EnableFeignClientsTests.java | 23 +- .../openfeign/FeignClientBuilderTests.java | 31 +- .../openfeign/FeignClientFactoryTests.java | 34 +- .../FeignClientOverrideDefaultsTests.java | 108 +- .../FeignClientUsingPropertiesTests.java | 75 +- .../openfeign/FeignClientsRegistrarTests.java | 46 +- .../openfeign/FeignCompressionTests.java | 17 +- .../FeignHttpClientConfigurationTests.java | 51 +- .../openfeign/FeignHttpClientUrlTests.java | 115 +- .../openfeign/FeignLoggerFactoryTests.java | 67 +- .../FeignOkHttpConfigurationTests.java | 36 +- .../cloud/openfeign/SpringDecoderTests.java | 141 +- .../openfeign/SpringRetryDisabledTests.java | 42 +- .../openfeign/SpringRetryEnabledTests.java | 29 +- .../openfeign/beans/FeignClientTests.java | 101 +- .../cloud/openfeign/beans/TestClient.java | 8 +- .../openfeign/beans/extra/TestClient.java | 4 +- .../encoding/FeignAcceptEncodingTests.java | 25 +- .../encoding/FeignContentEncodingTests.java | 32 +- .../cloud/openfeign/encoding/Invoices.java | 10 +- .../encoding/app/client/InvoiceClient.java | 3 +- .../encoding/app/domain/Invoice.java | 7 +- .../app/resource/InvoiceResource.java | 19 +- .../proto/ProtobufNotInClasspathTest.java | 26 +- .../proto/ProtobufSpringEncoderTest.java | 156 +- .../encoding/proto/ProtobufTest.java | 89 +- .../openfeign/encoding/proto/Request.java | 1049 +++---- .../encoding/proto/RequestOrBuilder.java | 33 +- .../security/HystrixSecurityApplication.java | 2 +- .../security/HystrixSecurityTests.java | 66 +- .../app/CustomConcurrenyStrategy.java | 24 +- .../security/app/ProxyUsernameController.java | 6 +- .../hystrix/security/app/TestInterceptor.java | 8 +- .../hystrix/security/app/UsernameClient.java | 3 +- .../security/app/UsernameController.java | 6 +- .../invalid/FeignClientValidationTests.java | 250 +- ...CachingSpringLoadBalancerFactoryTests.java | 24 +- .../ribbon/FeignLoadBalancerTests.java | 68 +- .../ribbon/FeignRibbonClientPathTests.java | 123 +- .../ribbon/FeignRibbonClientRetryTests.java | 71 +- .../ribbon/FeignRibbonClientTests.java | 57 +- ...ignRibbonHttpClientConfigurationTests.java | 51 +- ...nRibbonOkHttpClientConfigurationTests.java | 33 +- .../LoadBalancerFeignClientOverrideTests.java | 71 +- .../RetryableFeignLoadBalancerTests.java | 575 ++-- ...RibbonResponseStatusCodeExceptionTest.java | 27 +- .../FeignHttpClientPropertiesTests.java | 69 +- .../openfeign/support/SpringEncoderTests.java | 126 +- .../support/SpringMvcContractTests.java | 806 +++--- .../ApacheHttpClientConfigurationTests.java | 66 +- .../test/NoSecurityConfiguration.java | 10 +- .../test/OkHttpClientConfigurationTests.java | 85 +- .../openfeign/test/TestAutoConfiguration.java | 19 +- .../openfeign/testclients/TestClient.java | 28 +- .../valid/FeignClientNotPrimaryTests.java | 74 +- .../openfeign/valid/FeignClientTests.java | 883 +++--- .../valid/FeignClientValidationTests.java | 80 +- .../openfeign/valid/FeignHttpClientTests.java | 142 +- .../openfeign/valid/FeignOkHttpTests.java | 139 +- .../scanning/FeignClientEnvVarTests.java | 51 +- .../scanning/FeignClientScanningTests.java | 43 +- .../resources/feign-properties.properties | 8 +- spring-cloud-openfeign-dependencies/pom.xml | 3 +- spring-cloud-starter-openfeign/pom.xml | 7 +- .../main/resources/META-INF/spring.provides | 2 +- src/checkstyle/checkstyle-suppressions.xml | 18 + 120 files changed, 6686 insertions(+), 4301 deletions(-) create mode 100644 .editorconfig create mode 100644 .springformat create mode 100644 src/checkstyle/checkstyle-suppressions.xml diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..0679d88a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,14 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = tab +indent_size = 4 +end_of_line = lf +insert_final_newline = true + +[*.yml] +indent_style = space +indent_size = 2 diff --git a/.settings.xml b/.settings.xml index 6c355129..6f542c94 100644 --- a/.settings.xml +++ b/.settings.xml @@ -1,66 +1,68 @@ - - - repo.spring.io - ${env.CI_DEPLOY_USERNAME} - ${env.CI_DEPLOY_PASSWORD} - - - - - - spring - true - - - spring-snapshots - Spring Snapshots - http://repo.spring.io/libs-snapshot-local - - true - - - - spring-milestones - Spring Milestones - http://repo.spring.io/libs-milestone-local - - false - - - - spring-releases - Spring Releases - http://repo.spring.io/release - - false - - - - - - spring-snapshots - Spring Snapshots - http://repo.spring.io/libs-snapshot-local - - true - - - - spring-milestones - Spring Milestones - http://repo.spring.io/libs-milestone-local - - false - - - - - + + + repo.spring.io + ${env.CI_DEPLOY_USERNAME} + ${env.CI_DEPLOY_PASSWORD} + + + + + + spring + + true + + + + spring-snapshots + Spring Snapshots + http://repo.spring.io/libs-snapshot-local + + true + + + + spring-milestones + Spring Milestones + http://repo.spring.io/libs-milestone-local + + false + + + + spring-releases + Spring Releases + http://repo.spring.io/release + + false + + + + + + spring-snapshots + Spring Snapshots + http://repo.spring.io/libs-snapshot-local + + true + + + + spring-milestones + Spring Milestones + http://repo.spring.io/libs-milestone-local + + false + + + + + diff --git a/.springformat b/.springformat new file mode 100644 index 00000000..e69de29b diff --git a/asciidoctor.css b/asciidoctor.css index 06d52e68..ee4f1d81 100644 --- a/asciidoctor.css +++ b/asciidoctor.css @@ -1,399 +1,2011 @@ /* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */ /* Remove the comments around the @import statement below when using this as a custom stylesheet */ /*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic|Noto+Serif:400,400italic,700,700italic|Droid+Sans+Mono:400";*/ -article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block} -audio,canvas,video{display:inline-block} -audio:not([controls]){display:none;height:0} -[hidden],template{display:none} -script{display:none!important} -html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%} -body{margin:0} -a{background:transparent} -a:focus{outline:thin dotted} -a:active,a:hover{outline:0} -h1{font-size:2em;margin:.67em 0} -abbr[title]{border-bottom:1px dotted} -b,strong{font-weight:bold} -dfn{font-style:italic} -hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0} -mark{background:#ff0;color:#000} -code,kbd,pre,samp{font-family:monospace;font-size:1em} -pre{white-space:pre-wrap} -q{quotes:"\201C" "\201D" "\2018" "\2019"} -small{font-size:80%} -sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} -sup{top:-.5em} -sub{bottom:-.25em} -img{border:0} -svg:not(:root){overflow:hidden} -figure{margin:0} -fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em} -legend{border:0;padding:0} -button,input,select,textarea{font-family:inherit;font-size:100%;margin:0} -button,input{line-height:normal} -button,select{text-transform:none} -button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer} -button[disabled],html input[disabled]{cursor:default} -input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0} -input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box} -input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none} -button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} -textarea{overflow:auto;vertical-align:top} -table{border-collapse:collapse;border-spacing:0} -*,*:before,*:after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box} -html,body{font-size:100%} -body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto} -a:hover{cursor:pointer} -img,object,embed{max-width:100%;height:auto} -object,embed{height:100%} -img{-ms-interpolation-mode:bicubic} -#map_canvas img,#map_canvas embed,#map_canvas object,.map_canvas img,.map_canvas embed,.map_canvas object{max-width:none!important} -.left{float:left!important} -.right{float:right!important} -.text-left{text-align:left!important} -.text-right{text-align:right!important} -.text-center{text-align:center!important} -.text-justify{text-align:justify!important} -.hide{display:none} -.antialiased,body{-webkit-font-smoothing:antialiased} -img{display:inline-block;vertical-align:middle} -textarea{height:auto;min-height:50px} -select{width:100%} -p.lead,.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{font-size:1.21875em;line-height:1.6} -.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em} -div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr} -a{color:#2156a5;text-decoration:underline;line-height:inherit} -a:hover,a:focus{color:#1d4b8f} -a img{border:none} -p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility} -p aside{font-size:.875em;line-height:1.35;font-style:italic} -h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em} -h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0} -h1{font-size:2.125em} -h2{font-size:1.6875em} -h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em} -h4,h5{font-size:1.125em} -h6{font-size:1em} -hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0} -em,i{font-style:italic;line-height:inherit} -strong,b{font-weight:bold;line-height:inherit} -small{font-size:60%;line-height:inherit} -code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)} -ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit} -ul,ol,ul.no-bullet,ol.no-bullet{margin-left:1.5em} -ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em} -ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit} -ul.square{list-style-type:square} -ul.circle{list-style-type:circle} -ul.disc{list-style-type:disc} -ul.no-bullet{list-style:none} -ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0} -dl dt{margin-bottom:.3125em;font-weight:bold} -dl dd{margin-bottom:1.25em} -abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help} -abbr{text-transform:none} -blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd} -blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)} -blockquote cite:before{content:"\2014 \0020"} -blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)} -blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)} -@media only screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2} -h1{font-size:2.75em} -h2{font-size:2.3125em} -h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em} -h4{font-size:1.4375em}}table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede} -table thead,table tfoot{background:#f7f8f7;font-weight:bold} -table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left} -table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)} -table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7} -table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6} -h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em} -h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400} -.clearfix:before,.clearfix:after,.float-group:before,.float-group:after{content:" ";display:table} -.clearfix:after,.float-group:after{clear:both} -*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed} -pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed} -.keyseq{color:rgba(51,51,51,.8)} -kbd{display:inline-block;color:rgba(0,0,0,.8);font-size:.75em;line-height:1.4;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:-.15em .15em 0 .15em;padding:.2em .6em .2em .5em;vertical-align:middle;white-space:nowrap} -.keyseq kbd:first-child{margin-left:0} -.keyseq kbd:last-child{margin-right:0} -.menuseq,.menu{color:rgba(0,0,0,.8)} -b.button:before,b.button:after{position:relative;top:-1px;font-weight:400} -b.button:before{content:"[";padding:0 3px 0 2px} -b.button:after{content:"]";padding:0 2px 0 3px} -p a>code:hover{color:rgba(0,0,0,.9)} -#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em} -#header:before,#header:after,#content:before,#content:after,#footnotes:before,#footnotes:after,#footer:before,#footer:after{content:" ";display:table} -#header:after,#content:after,#footnotes:after,#footer:after{clear:both} -#content{margin-top:1.25em} -#content:before{content:none} -#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0} -#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8} -#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px} -#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap} -#header .details span:first-child{margin-left:-.125em} -#header .details span.email a{color:rgba(0,0,0,.85)} -#header .details br{display:none} -#header .details br+span:before{content:"\00a0\2013\00a0"} -#header .details br+span.author:before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)} -#header .details br+span#revremark:before{content:"\00a0|\00a0"} -#header #revnumber{text-transform:capitalize} -#header #revnumber:after{content:"\00a0"} -#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem} -#toc{border-bottom:1px solid #efefed;padding-bottom:.5em} -#toc>ul{margin-left:.125em} -#toc ul.sectlevel0>li>a{font-style:italic} -#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0} -#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none} -#toc a{text-decoration:none} -#toc a:active{text-decoration:underline} -#toctitle{color:#7a2518;font-size:1.2em} -@media only screen and (min-width:768px){#toctitle{font-size:1.375em} -body.toc2{padding-left:15em;padding-right:0} -#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto} -#toc.toc2 #toctitle{margin-top:0;font-size:1.2em} -#toc.toc2>ul{font-size:.9em;margin-bottom:0} -#toc.toc2 ul ul{margin-left:0;padding-left:1em} -#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em} -body.toc2.toc-right{padding-left:0;padding-right:15em} -body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}@media only screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0} -#toc.toc2{width:20em} -#toc.toc2 #toctitle{font-size:1.375em} -#toc.toc2>ul{font-size:.95em} -#toc.toc2 ul ul{padding-left:1.25em} -body.toc2.toc-right{padding-left:0;padding-right:20em}}#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px} -#content #toc>:first-child{margin-top:0} -#content #toc>:last-child{margin-bottom:0} -#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em} -#footer-text{color:rgba(255,255,255,.8);line-height:1.44} -.sect1{padding-bottom:.625em} -@media only screen and (min-width:768px){.sect1{padding-bottom:1.25em}}.sect1+.sect1{border-top:1px solid #efefed} -#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400} -#content h1>a.anchor:before,h2>a.anchor:before,h3>a.anchor:before,#toctitle>a.anchor:before,.sidebarblock>.content>.title>a.anchor:before,h4>a.anchor:before,h5>a.anchor:before,h6>a.anchor:before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em} -#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible} -#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none} -#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221} -.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em} -.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic} -table.tableblock>caption.title{white-space:nowrap;overflow:visible;max-width:0} -.paragraph.lead>p,#preamble>.sectionbody>.paragraph:first-of-type p{color:rgba(0,0,0,.85)} -table.tableblock #preamble>.sectionbody>.paragraph:first-of-type p{font-size:inherit} -.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%} -.admonitionblock>table td.icon{text-align:center;width:80px} -.admonitionblock>table td.icon img{max-width:none} -.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase} -.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)} -.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0} -.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px} -.exampleblock>.content>:first-child{margin-top:0} -.exampleblock>.content>:last-child{margin-bottom:0} -.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px} -.sidebarblock>:first-child{margin-top:0} -.sidebarblock>:last-child{margin-bottom:0} -.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center} -.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0} -.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8} -.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1} -.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em} -.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal} -@media only screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}@media only screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)} -.listingblock pre.highlightjs{padding:0} -.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px} -.listingblock pre.prettyprint{border-width:0} -.listingblock>.content{position:relative} -.listingblock code[data-lang]:before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999} -.listingblock:hover code[data-lang]:before{display:block} -.listingblock.terminal pre .command:before{content:attr(data-prompt);padding-right:.5em;color:#999} -.listingblock.terminal pre .command:not([data-prompt]):before{content:"$"} -table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none} -table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0} -table.pyhltable td.code{padding-left:.75em;padding-right:0} -pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8} -pre.pygments .lineno{display:inline-block;margin-right:.25em} -table.pyhltable .linenodiv{background:none!important;padding-right:0!important} -.quoteblock{margin:0 1em 1.25em 1.5em;display:table} -.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em} -.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify} -.quoteblock blockquote{margin:0;padding:0;border:0} -.quoteblock blockquote:before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)} -.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0} -.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right} -.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)} -.quoteblock .quoteblock blockquote{padding:0 0 0 .75em} -.quoteblock .quoteblock blockquote:before{display:none} -.verseblock{margin:0 1em 1.25em 1em} -.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility} -.verseblock pre strong{font-weight:400} -.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex} -.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic} -.quoteblock .attribution br,.verseblock .attribution br{display:none} -.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.05em;color:rgba(0,0,0,.6)} -.quoteblock.abstract{margin:0 0 1.25em 0;display:block} -.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{text-align:left;word-spacing:0} -.quoteblock.abstract blockquote:before,.quoteblock.abstract blockquote p:first-of-type:before{display:none} -table.tableblock{max-width:100%;border-collapse:separate} -table.tableblock td>.paragraph:last-child p>p:last-child,table.tableblock th>p:last-child,table.tableblock td>p:last-child{margin-bottom:0} -table.spread{width:100%} -table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede} -table.grid-all th.tableblock,table.grid-all td.tableblock{border-width:0 1px 1px 0} -table.grid-all tfoot>tr>th.tableblock,table.grid-all tfoot>tr>td.tableblock{border-width:1px 1px 0 0} -table.grid-cols th.tableblock,table.grid-cols td.tableblock{border-width:0 1px 0 0} -table.grid-all *>tr>.tableblock:last-child,table.grid-cols *>tr>.tableblock:last-child{border-right-width:0} -table.grid-rows th.tableblock,table.grid-rows td.tableblock{border-width:0 0 1px 0} -table.grid-all tbody>tr:last-child>th.tableblock,table.grid-all tbody>tr:last-child>td.tableblock,table.grid-all thead:last-child>tr>th.tableblock,table.grid-rows tbody>tr:last-child>th.tableblock,table.grid-rows tbody>tr:last-child>td.tableblock,table.grid-rows thead:last-child>tr>th.tableblock{border-bottom-width:0} -table.grid-rows tfoot>tr>th.tableblock,table.grid-rows tfoot>tr>td.tableblock{border-width:1px 0 0 0} -table.frame-all{border-width:1px} -table.frame-sides{border-width:0 1px} -table.frame-topbot{border-width:1px 0} -th.halign-left,td.halign-left{text-align:left} -th.halign-right,td.halign-right{text-align:right} -th.halign-center,td.halign-center{text-align:center} -th.valign-top,td.valign-top{vertical-align:top} -th.valign-bottom,td.valign-bottom{vertical-align:bottom} -th.valign-middle,td.valign-middle{vertical-align:middle} -table thead th,table tfoot th{font-weight:bold} -tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7} -tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold} -p.tableblock>code:only-child{background:none;padding:0} -p.tableblock{font-size:1em} -td>div.verse{white-space:pre} -ol{margin-left:1.75em} -ul li ol{margin-left:1.5em} -dl dd{margin-left:1.125em} -dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0} -ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em} -ul.unstyled,ol.unnumbered,ul.checklist,ul.none{list-style-type:none} -ul.unstyled,ol.unnumbered,ul.checklist{margin-left:.625em} -ul.checklist li>p:first-child>.fa-check-square-o:first-child,ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em} -ul.checklist li>p:first-child>input[type="checkbox"]:first-child{position:relative;top:1px} -ul.inline{margin:0 auto .625em auto;margin-left:-1.375em;margin-right:0;padding:0;list-style:none;overflow:hidden} -ul.inline>li{list-style:none;float:left;margin-left:1.375em;display:block} -ul.inline>li>*{display:block} -.unstyled dl dt{font-weight:400;font-style:normal} -ol.arabic{list-style-type:decimal} -ol.decimal{list-style-type:decimal-leading-zero} -ol.loweralpha{list-style-type:lower-alpha} -ol.upperalpha{list-style-type:upper-alpha} -ol.lowerroman{list-style-type:lower-roman} -ol.upperroman{list-style-type:upper-roman} -ol.lowergreek{list-style-type:lower-greek} -.hdlist>table,.colist>table{border:0;background:none} -.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none} -td.hdlist1{padding-right:.75em;font-weight:bold} -td.hdlist1,td.hdlist2{vertical-align:top} -.literalblock+.colist,.listingblock+.colist{margin-top:-.5em} -.colist>table tr>td:first-of-type{padding:0 .75em;line-height:1} -.colist>table tr>td:last-of-type{padding:.25em 0} -.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd} -.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0} -.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em} -.imageblock>.title{margin-bottom:0} -.imageblock.thumb,.imageblock.th{border-width:6px} -.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em} -.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0} -.image.left{margin-right:.625em} -.image.right{margin-left:.625em} -a.image{text-decoration:none} -span.footnote,span.footnoteref{vertical-align:super;font-size:.875em} -span.footnote a,span.footnoteref a{text-decoration:none} -span.footnote a:active,span.footnoteref a:active{text-decoration:underline} -#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em} -#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em 0;border-width:1px 0 0 0} -#footnotes .footnote{padding:0 .375em;line-height:1.3;font-size:.875em;margin-left:1.2em;text-indent:-1.2em;margin-bottom:.2em} -#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none} -#footnotes .footnote:last-of-type{margin-bottom:0} -#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0} -.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0} -.gist .file-data>table td.line-data{width:99%} -div.unbreakable{page-break-inside:avoid} -.big{font-size:larger} -.small{font-size:smaller} -.underline{text-decoration:underline} -.overline{text-decoration:overline} -.line-through{text-decoration:line-through} -.aqua{color:#00bfbf} -.aqua-background{background-color:#00fafa} -.black{color:#000} -.black-background{background-color:#000} -.blue{color:#0000bf} -.blue-background{background-color:#0000fa} -.fuchsia{color:#bf00bf} -.fuchsia-background{background-color:#fa00fa} -.gray{color:#606060} -.gray-background{background-color:#7d7d7d} -.green{color:#006000} -.green-background{background-color:#007d00} -.lime{color:#00bf00} -.lime-background{background-color:#00fa00} -.maroon{color:#600000} -.maroon-background{background-color:#7d0000} -.navy{color:#000060} -.navy-background{background-color:#00007d} -.olive{color:#606000} -.olive-background{background-color:#7d7d00} -.purple{color:#600060} -.purple-background{background-color:#7d007d} -.red{color:#bf0000} -.red-background{background-color:#fa0000} -.silver{color:#909090} -.silver-background{background-color:#bcbcbc} -.teal{color:#006060} -.teal-background{background-color:#007d7d} -.white{color:#bfbfbf} -.white-background{background-color:#fafafa} -.yellow{color:#bfbf00} -.yellow-background{background-color:#fafa00} -span.icon>.fa{cursor:default} -.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default} -.admonitionblock td.icon .icon-note:before{content:"\f05a";color:#19407c} -.admonitionblock td.icon .icon-tip:before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111} -.admonitionblock td.icon .icon-warning:before{content:"\f071";color:#bf6900} -.admonitionblock td.icon .icon-caution:before{content:"\f06d";color:#bf3400} -.admonitionblock td.icon .icon-important:before{content:"\f06a";color:#bf0000} -.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold} -.conum[data-value] *{color:#fff!important} -.conum[data-value]+b{display:none} -.conum[data-value]:after{content:attr(data-value)} -pre .conum[data-value]{position:relative;top:-.125em} -b.conum *{color:inherit!important} -.conum:not([data-value]):empty{display:none} -h1,h2{letter-spacing:-.01em} -dt,th.tableblock,td.content{text-rendering:optimizeLegibility} -p,td.content{letter-spacing:-.01em} -p strong,td.content strong{letter-spacing:-.005em} -p,blockquote,dt,td.content{font-size:1.0625rem} -p{margin-bottom:1.25rem} -.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em} -.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc} -.print-only{display:none!important} -@media print{@page{margin:1.25cm .75cm} -*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important} -a{color:inherit!important;text-decoration:underline!important} -a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important} -a[href^="http:"]:not(.bare):after,a[href^="https:"]:not(.bare):after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em} -abbr[title]:after{content:" (" attr(title) ")"} -pre,blockquote,tr,img{page-break-inside:avoid} -thead{display:table-header-group} -img{max-width:100%!important} -p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3} -h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid} -#toc,.sidebarblock,.exampleblock>.content{background:none!important} -#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important} -.sect1{padding-bottom:0!important} -.sect1+.sect1{border:0!important} -#header>h1:first-child{margin-top:1.25rem} -body.book #header{text-align:center} -body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em 0} -body.book #header .details{border:0!important;display:block;padding:0!important} -body.book #header .details span:first-child{margin-left:0!important} -body.book #header .details br{display:block} -body.book #header .details br+span:before{content:none!important} -body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important} -body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always} -.listingblock code[data-lang]:before{display:block} -#footer{background:none!important;padding:0 .9375em} -#footer-text{color:rgba(0,0,0,.6)!important;font-size:.9em} -.hide-on-print{display:none!important} -.print-only{display:block!important} -.hide-for-print{display:none!important} -.show-for-print{display:inherit!important}} \ No newline at end of file +article, aside, details, figcaption, figure, footer, header, hgroup, main, nav, section, summary { + display: block +} + +audio, canvas, video { + display: inline-block +} + +audio:not([controls]) { + display: none; + height: 0 +} + +[hidden], template { + display: none +} + +script { + display: none !important +} + +html { + font-family: sans-serif; + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100% +} + +body { + margin: 0 +} + +a { + background: transparent +} + +a:focus { + outline: thin dotted +} + +a:active, a:hover { + outline: 0 +} + +h1 { + font-size: 2em; + margin: .67em 0 +} + +abbr[title] { + border-bottom: 1px dotted +} + +b, strong { + font-weight: bold +} + +dfn { + font-style: italic +} + +hr { + -moz-box-sizing: content-box; + box-sizing: content-box; + height: 0 +} + +mark { + background: #ff0; + color: #000 +} + +code, kbd, pre, samp { + font-family: monospace; + font-size: 1em +} + +pre { + white-space: pre-wrap +} + +q { + quotes: "\201C" "\201D" "\2018" "\2019" +} + +small { + font-size: 80% +} + +sub, sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline +} + +sup { + top: -.5em +} + +sub { + bottom: -.25em +} + +img { + border: 0 +} + +svg:not(:root) { + overflow: hidden +} + +figure { + margin: 0 +} + +fieldset { + border: 1px solid silver; + margin: 0 2px; + padding: .35em .625em .75em +} + +legend { + border: 0; + padding: 0 +} + +button, input, select, textarea { + font-family: inherit; + font-size: 100%; + margin: 0 +} + +button, input { + line-height: normal +} + +button, select { + text-transform: none +} + +button, html input[type="button"], input[type="reset"], input[type="submit"] { + -webkit-appearance: button; + cursor: pointer +} + +button[disabled], html input[disabled] { + cursor: default +} + +input[type="checkbox"], input[type="radio"] { + box-sizing: border-box; + padding: 0 +} + +input[type="search"] { + -webkit-appearance: textfield; + -moz-box-sizing: content-box; + -webkit-box-sizing: content-box; + box-sizing: content-box +} + +input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none +} + +button::-moz-focus-inner, input::-moz-focus-inner { + border: 0; + padding: 0 +} + +textarea { + overflow: auto; + vertical-align: top +} + +table { + border-collapse: collapse; + border-spacing: 0 +} + +*, *:before, *:after { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box +} + +html, body { + font-size: 100% +} + +body { + background: #fff; + color: rgba(0, 0, 0, .8); + padding: 0; + margin: 0; + font-family: "Noto Serif", "DejaVu Serif", serif; + font-weight: 400; + font-style: normal; + line-height: 1; + position: relative; + cursor: auto +} + +a:hover { + cursor: pointer +} + +img, object, embed { + max-width: 100%; + height: auto +} + +object, embed { + height: 100% +} + +img { + -ms-interpolation-mode: bicubic +} + +#map_canvas img, #map_canvas embed, #map_canvas object, .map_canvas img, .map_canvas embed, .map_canvas object { + max-width: none !important +} + +.left { + float: left !important +} + +.right { + float: right !important +} + +.text-left { + text-align: left !important +} + +.text-right { + text-align: right !important +} + +.text-center { + text-align: center !important +} + +.text-justify { + text-align: justify !important +} + +.hide { + display: none +} + +.antialiased, body { + -webkit-font-smoothing: antialiased +} + +img { + display: inline-block; + vertical-align: middle +} + +textarea { + height: auto; + min-height: 50px +} + +select { + width: 100% +} + +p.lead, .paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { + font-size: 1.21875em; + line-height: 1.6 +} + +.subheader, .admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { + line-height: 1.45; + color: #7a2518; + font-weight: 400; + margin-top: 0; + margin-bottom: .25em +} + +div, dl, dt, dd, ul, ol, li, h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6, pre, form, p, blockquote, th, td { + margin: 0; + padding: 0; + direction: ltr +} + +a { + color: #2156a5; + text-decoration: underline; + line-height: inherit +} + +a:hover, a:focus { + color: #1d4b8f +} + +a img { + border: none +} + +p { + font-family: inherit; + font-weight: 400; + font-size: 1em; + line-height: 1.6; + margin-bottom: 1.25em; + text-rendering: optimizeLegibility +} + +p aside { + font-size: .875em; + line-height: 1.35; + font-style: italic +} + +h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { + font-family: "Open Sans", "DejaVu Sans", sans-serif; + font-weight: 300; + font-style: normal; + color: #ba3925; + text-rendering: optimizeLegibility; + margin-top: 1em; + margin-bottom: .5em; + line-height: 1.0125em +} + +h1 small, h2 small, h3 small, #toctitle small, .sidebarblock > .content > .title small, h4 small, h5 small, h6 small { + font-size: 60%; + color: #e99b8f; + line-height: 0 +} + +h1 { + font-size: 2.125em +} + +h2 { + font-size: 1.6875em +} + +h3, #toctitle, .sidebarblock > .content > .title { + font-size: 1.375em +} + +h4, h5 { + font-size: 1.125em +} + +h6 { + font-size: 1em +} + +hr { + border: solid #ddddd8; + border-width: 1px 0 0; + clear: both; + margin: 1.25em 0 1.1875em; + height: 0 +} + +em, i { + font-style: italic; + line-height: inherit +} + +strong, b { + font-weight: bold; + line-height: inherit +} + +small { + font-size: 60%; + line-height: inherit +} + +code { + font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; + font-weight: 400; + color: rgba(0, 0, 0, .9) +} + +ul, ol, dl { + font-size: 1em; + line-height: 1.6; + margin-bottom: 1.25em; + list-style-position: outside; + font-family: inherit +} + +ul, ol, ul.no-bullet, ol.no-bullet { + margin-left: 1.5em +} + +ul li ul, ul li ol { + margin-left: 1.25em; + margin-bottom: 0; + font-size: 1em +} + +ul.square li ul, ul.circle li ul, ul.disc li ul { + list-style: inherit +} + +ul.square { + list-style-type: square +} + +ul.circle { + list-style-type: circle +} + +ul.disc { + list-style-type: disc +} + +ul.no-bullet { + list-style: none +} + +ol li ul, ol li ol { + margin-left: 1.25em; + margin-bottom: 0 +} + +dl dt { + margin-bottom: .3125em; + font-weight: bold +} + +dl dd { + margin-bottom: 1.25em +} + +abbr, acronym { + text-transform: uppercase; + font-size: 90%; + color: rgba(0, 0, 0, .8); + border-bottom: 1px dotted #ddd; + cursor: help +} + +abbr { + text-transform: none +} + +blockquote { + margin: 0 0 1.25em; + padding: .5625em 1.25em 0 1.1875em; + border-left: 1px solid #ddd +} + +blockquote cite { + display: block; + font-size: .9375em; + color: rgba(0, 0, 0, .6) +} + +blockquote cite:before { + content: "\2014 \0020" +} + +blockquote cite a, blockquote cite a:visited { + color: rgba(0, 0, 0, .6) +} + +blockquote, blockquote p { + line-height: 1.6; + color: rgba(0, 0, 0, .85) +} + +@media only screen and (min-width: 768px) { + h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { + line-height: 1.2 + } + + h1 { + font-size: 2.75em + } + + h2 { + font-size: 2.3125em + } + + h3, #toctitle, .sidebarblock > .content > .title { + font-size: 1.6875em + } + + h4 { + font-size: 1.4375em + } +} + +table { + background: #fff; + margin-bottom: 1.25em; + border: solid 1px #dedede +} + +table thead, table tfoot { + background: #f7f8f7; + font-weight: bold +} + +table thead tr th, table thead tr td, table tfoot tr th, table tfoot tr td { + padding: .5em .625em .625em; + font-size: inherit; + color: rgba(0, 0, 0, .8); + text-align: left +} + +table tr th, table tr td { + padding: .5625em .625em; + font-size: inherit; + color: rgba(0, 0, 0, .8) +} + +table tr.even, table tr.alt, table tr:nth-of-type(even) { + background: #f8f8f7 +} + +table thead tr th, table tfoot tr th, table tbody tr td, table tr td, table tfoot tr td { + display: table-cell; + line-height: 1.6 +} + +h1, h2, h3, #toctitle, .sidebarblock > .content > .title, h4, h5, h6 { + line-height: 1.2; + word-spacing: -.05em +} + +h1 strong, h2 strong, h3 strong, #toctitle strong, .sidebarblock > .content > .title strong, h4 strong, h5 strong, h6 strong { + font-weight: 400 +} + +.clearfix:before, .clearfix:after, .float-group:before, .float-group:after { + content: " "; + display: table +} + +.clearfix:after, .float-group:after { + clear: both +} + +*:not(pre) > code { + font-size: .9375em; + font-style: normal !important; + letter-spacing: 0; + padding: .1em .5ex; + word-spacing: -.15em; + background-color: #f7f7f8; + -webkit-border-radius: 4px; + border-radius: 4px; + line-height: 1.45; + text-rendering: optimizeSpeed +} + +pre, pre > code { + line-height: 1.45; + color: rgba(0, 0, 0, .9); + font-family: "Droid Sans Mono", "DejaVu Sans Mono", monospace; + font-weight: 400; + text-rendering: optimizeSpeed +} + +.keyseq { + color: rgba(51, 51, 51, .8) +} + +kbd { + display: inline-block; + color: rgba(0, 0, 0, .8); + font-size: .75em; + line-height: 1.4; + background-color: #f7f7f7; + border: 1px solid #ccc; + -webkit-border-radius: 3px; + border-radius: 3px; + -webkit-box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em white inset; + box-shadow: 0 1px 0 rgba(0, 0, 0, .2), 0 0 0 .1em #fff inset; + margin: -.15em .15em 0 .15em; + padding: .2em .6em .2em .5em; + vertical-align: middle; + white-space: nowrap +} + +.keyseq kbd:first-child { + margin-left: 0 +} + +.keyseq kbd:last-child { + margin-right: 0 +} + +.menuseq, .menu { + color: rgba(0, 0, 0, .8) +} + +b.button:before, b.button:after { + position: relative; + top: -1px; + font-weight: 400 +} + +b.button:before { + content: "["; + padding: 0 3px 0 2px +} + +b.button:after { + content: "]"; + padding: 0 2px 0 3px +} + +p a > code:hover { + color: rgba(0, 0, 0, .9) +} + +#header, #content, #footnotes, #footer { + width: 100%; + margin-left: auto; + margin-right: auto; + margin-top: 0; + margin-bottom: 0; + max-width: 62.5em; + *zoom: 1; + position: relative; + padding-left: .9375em; + padding-right: .9375em +} + +#header:before, #header:after, #content:before, #content:after, #footnotes:before, #footnotes:after, #footer:before, #footer:after { + content: " "; + display: table +} + +#header:after, #content:after, #footnotes:after, #footer:after { + clear: both +} + +#content { + margin-top: 1.25em +} + +#content:before { + content: none +} + +#header > h1:first-child { + color: rgba(0, 0, 0, .85); + margin-top: 2.25rem; + margin-bottom: 0 +} + +#header > h1:first-child + #toc { + margin-top: 8px; + border-top: 1px solid #ddddd8 +} + +#header > h1:only-child, body.toc2 #header > h1:nth-last-child(2) { + border-bottom: 1px solid #ddddd8; + padding-bottom: 8px +} + +#header .details { + border-bottom: 1px solid #ddddd8; + line-height: 1.45; + padding-top: .25em; + padding-bottom: .25em; + padding-left: .25em; + color: rgba(0, 0, 0, .6); + display: -ms-flexbox; + display: -webkit-flex; + display: flex; + -ms-flex-flow: row wrap; + -webkit-flex-flow: row wrap; + flex-flow: row wrap +} + +#header .details span:first-child { + margin-left: -.125em +} + +#header .details span.email a { + color: rgba(0, 0, 0, .85) +} + +#header .details br { + display: none +} + +#header .details br + span:before { + content: "\00a0\2013\00a0" +} + +#header .details br + span.author:before { + content: "\00a0\22c5\00a0"; + color: rgba(0, 0, 0, .85) +} + +#header .details br + span#revremark:before { + content: "\00a0|\00a0" +} + +#header #revnumber { + text-transform: capitalize +} + +#header #revnumber:after { + content: "\00a0" +} + +#content > h1:first-child:not([class]) { + color: rgba(0, 0, 0, .85); + border-bottom: 1px solid #ddddd8; + padding-bottom: 8px; + margin-top: 0; + padding-top: 1rem; + margin-bottom: 1.25rem +} + +#toc { + border-bottom: 1px solid #efefed; + padding-bottom: .5em +} + +#toc > ul { + margin-left: .125em +} + +#toc ul.sectlevel0 > li > a { + font-style: italic +} + +#toc ul.sectlevel0 ul.sectlevel1 { + margin: .5em 0 +} + +#toc ul { + font-family: "Open Sans", "DejaVu Sans", sans-serif; + list-style-type: none +} + +#toc a { + text-decoration: none +} + +#toc a:active { + text-decoration: underline +} + +#toctitle { + color: #7a2518; + font-size: 1.2em +} + +@media only screen and (min-width: 768px) { + #toctitle { + font-size: 1.375em + } + + body.toc2 { + padding-left: 15em; + padding-right: 0 + } + + #toc.toc2 { + margin-top: 0 !important; + background-color: #f8f8f7; + position: fixed; + width: 15em; + left: 0; + top: 0; + border-right: 1px solid #efefed; + border-top-width: 0 !important; + border-bottom-width: 0 !important; + z-index: 1000; + padding: 1.25em 1em; + height: 100%; + overflow: auto + } + + #toc.toc2 #toctitle { + margin-top: 0; + font-size: 1.2em + } + + #toc.toc2 > ul { + font-size: .9em; + margin-bottom: 0 + } + + #toc.toc2 ul ul { + margin-left: 0; + padding-left: 1em + } + + #toc.toc2 ul.sectlevel0 ul.sectlevel1 { + padding-left: 0; + margin-top: .5em; + margin-bottom: .5em + } + + body.toc2.toc-right { + padding-left: 0; + padding-right: 15em + } + + body.toc2.toc-right #toc.toc2 { + border-right-width: 0; + border-left: 1px solid #efefed; + left: auto; + right: 0 + } +} + +@media only screen and (min-width: 1280px) { + body.toc2 { + padding-left: 20em; + padding-right: 0 + } + + #toc.toc2 { + width: 20em + } + + #toc.toc2 #toctitle { + font-size: 1.375em + } + + #toc.toc2 > ul { + font-size: .95em + } + + #toc.toc2 ul ul { + padding-left: 1.25em + } + + body.toc2.toc-right { + padding-left: 0; + padding-right: 20em + } +} + +#content #toc { + border-style: solid; + border-width: 1px; + border-color: #e0e0dc; + margin-bottom: 1.25em; + padding: 1.25em; + background: #f8f8f7; + -webkit-border-radius: 4px; + border-radius: 4px +} + +#content #toc > :first-child { + margin-top: 0 +} + +#content #toc > :last-child { + margin-bottom: 0 +} + +#footer { + max-width: 100%; + background-color: rgba(0, 0, 0, .8); + padding: 1.25em +} + +#footer-text { + color: rgba(255, 255, 255, .8); + line-height: 1.44 +} + +.sect1 { + padding-bottom: .625em +} + +@media only screen and (min-width: 768px) { + .sect1 { + padding-bottom: 1.25em + } +} + +.sect1 + .sect1 { + border-top: 1px solid #efefed +} + +#content h1 > a.anchor, h2 > a.anchor, h3 > a.anchor, #toctitle > a.anchor, .sidebarblock > .content > .title > a.anchor, h4 > a.anchor, h5 > a.anchor, h6 > a.anchor { + position: absolute; + z-index: 1001; + width: 1.5ex; + margin-left: -1.5ex; + display: block; + text-decoration: none !important; + visibility: hidden; + text-align: center; + font-weight: 400 +} + +#content h1 > a.anchor:before, h2 > a.anchor:before, h3 > a.anchor:before, #toctitle > a.anchor:before, .sidebarblock > .content > .title > a.anchor:before, h4 > a.anchor:before, h5 > a.anchor:before, h6 > a.anchor:before { + content: "\00A7"; + font-size: .85em; + display: block; + padding-top: .1em +} + +#content h1:hover > a.anchor, #content h1 > a.anchor:hover, h2:hover > a.anchor, h2 > a.anchor:hover, h3:hover > a.anchor, #toctitle:hover > a.anchor, .sidebarblock > .content > .title:hover > a.anchor, h3 > a.anchor:hover, #toctitle > a.anchor:hover, .sidebarblock > .content > .title > a.anchor:hover, h4:hover > a.anchor, h4 > a.anchor:hover, h5:hover > a.anchor, h5 > a.anchor:hover, h6:hover > a.anchor, h6 > a.anchor:hover { + visibility: visible +} + +#content h1 > a.link, h2 > a.link, h3 > a.link, #toctitle > a.link, .sidebarblock > .content > .title > a.link, h4 > a.link, h5 > a.link, h6 > a.link { + color: #ba3925; + text-decoration: none +} + +#content h1 > a.link:hover, h2 > a.link:hover, h3 > a.link:hover, #toctitle > a.link:hover, .sidebarblock > .content > .title > a.link:hover, h4 > a.link:hover, h5 > a.link:hover, h6 > a.link:hover { + color: #a53221 +} + +.audioblock, .imageblock, .literalblock, .listingblock, .stemblock, .videoblock { + margin-bottom: 1.25em +} + +.admonitionblock td.content > .title, .audioblock > .title, .exampleblock > .title, .imageblock > .title, .listingblock > .title, .literalblock > .title, .stemblock > .title, .openblock > .title, .paragraph > .title, .quoteblock > .title, table.tableblock > .title, .verseblock > .title, .videoblock > .title, .dlist > .title, .olist > .title, .ulist > .title, .qlist > .title, .hdlist > .title { + text-rendering: optimizeLegibility; + text-align: left; + font-family: "Noto Serif", "DejaVu Serif", serif; + font-size: 1rem; + font-style: italic +} + +table.tableblock > caption.title { + white-space: nowrap; + overflow: visible; + max-width: 0 +} + +.paragraph.lead > p, #preamble > .sectionbody > .paragraph:first-of-type p { + color: rgba(0, 0, 0, .85) +} + +table.tableblock #preamble > .sectionbody > .paragraph:first-of-type p { + font-size: inherit +} + +.admonitionblock > table { + border-collapse: separate; + border: 0; + background: none; + width: 100% +} + +.admonitionblock > table td.icon { + text-align: center; + width: 80px +} + +.admonitionblock > table td.icon img { + max-width: none +} + +.admonitionblock > table td.icon .title { + font-weight: bold; + font-family: "Open Sans", "DejaVu Sans", sans-serif; + text-transform: uppercase +} + +.admonitionblock > table td.content { + padding-left: 1.125em; + padding-right: 1.25em; + border-left: 1px solid #ddddd8; + color: rgba(0, 0, 0, .6) +} + +.admonitionblock > table td.content > :last-child > :last-child { + margin-bottom: 0 +} + +.exampleblock > .content { + border-style: solid; + border-width: 1px; + border-color: #e6e6e6; + margin-bottom: 1.25em; + padding: 1.25em; + background: #fff; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.exampleblock > .content > :first-child { + margin-top: 0 +} + +.exampleblock > .content > :last-child { + margin-bottom: 0 +} + +.sidebarblock { + border-style: solid; + border-width: 1px; + border-color: #e0e0dc; + margin-bottom: 1.25em; + padding: 1.25em; + background: #f8f8f7; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.sidebarblock > :first-child { + margin-top: 0 +} + +.sidebarblock > :last-child { + margin-bottom: 0 +} + +.sidebarblock > .content > .title { + color: #7a2518; + margin-top: 0; + text-align: center +} + +.exampleblock > .content > :last-child > :last-child, .exampleblock > .content .olist > ol > li:last-child > :last-child, .exampleblock > .content .ulist > ul > li:last-child > :last-child, .exampleblock > .content .qlist > ol > li:last-child > :last-child, .sidebarblock > .content > :last-child > :last-child, .sidebarblock > .content .olist > ol > li:last-child > :last-child, .sidebarblock > .content .ulist > ul > li:last-child > :last-child, .sidebarblock > .content .qlist > ol > li:last-child > :last-child { + margin-bottom: 0 +} + +.literalblock pre, .listingblock pre:not(.highlight), .listingblock pre[class="highlight"], .listingblock pre[class^="highlight "], .listingblock pre.CodeRay, .listingblock pre.prettyprint { + background: #f7f7f8 +} + +.sidebarblock .literalblock pre, .sidebarblock .listingblock pre:not(.highlight), .sidebarblock .listingblock pre[class="highlight"], .sidebarblock .listingblock pre[class^="highlight "], .sidebarblock .listingblock pre.CodeRay, .sidebarblock .listingblock pre.prettyprint { + background: #f2f1f1 +} + +.literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { + -webkit-border-radius: 4px; + border-radius: 4px; + word-wrap: break-word; + padding: 1em; + font-size: .8125em +} + +.literalblock pre.nowrap, .literalblock pre[class].nowrap, .listingblock pre.nowrap, .listingblock pre[class].nowrap { + overflow-x: auto; + white-space: pre; + word-wrap: normal +} + +@media only screen and (min-width: 768px) { + .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { + font-size: .90625em + } +} + +@media only screen and (min-width: 1280px) { + .literalblock pre, .literalblock pre[class], .listingblock pre, .listingblock pre[class] { + font-size: 1em + } +} + +.literalblock.output pre { + color: #f7f7f8; + background-color: rgba(0, 0, 0, .9) +} + +.listingblock pre.highlightjs { + padding: 0 +} + +.listingblock pre.highlightjs > code { + padding: 1em; + -webkit-border-radius: 4px; + border-radius: 4px +} + +.listingblock pre.prettyprint { + border-width: 0 +} + +.listingblock > .content { + position: relative +} + +.listingblock code[data-lang]:before { + display: none; + content: attr(data-lang); + position: absolute; + font-size: .75em; + top: .425rem; + right: .5rem; + line-height: 1; + text-transform: uppercase; + color: #999 +} + +.listingblock:hover code[data-lang]:before { + display: block +} + +.listingblock.terminal pre .command:before { + content: attr(data-prompt); + padding-right: .5em; + color: #999 +} + +.listingblock.terminal pre .command:not([data-prompt]):before { + content: "$" +} + +table.pyhltable { + border-collapse: separate; + border: 0; + margin-bottom: 0; + background: none +} + +table.pyhltable td { + vertical-align: top; + padding-top: 0; + padding-bottom: 0 +} + +table.pyhltable td.code { + padding-left: .75em; + padding-right: 0 +} + +pre.pygments .lineno, table.pyhltable td:not(.code) { + color: #999; + padding-left: 0; + padding-right: .5em; + border-right: 1px solid #ddddd8 +} + +pre.pygments .lineno { + display: inline-block; + margin-right: .25em +} + +table.pyhltable .linenodiv { + background: none !important; + padding-right: 0 !important +} + +.quoteblock { + margin: 0 1em 1.25em 1.5em; + display: table +} + +.quoteblock > .title { + margin-left: -1.5em; + margin-bottom: .75em +} + +.quoteblock blockquote, .quoteblock blockquote p { + color: rgba(0, 0, 0, .85); + font-size: 1.15rem; + line-height: 1.75; + word-spacing: .1em; + letter-spacing: 0; + font-style: italic; + text-align: justify +} + +.quoteblock blockquote { + margin: 0; + padding: 0; + border: 0 +} + +.quoteblock blockquote:before { + content: "\201c"; + float: left; + font-size: 2.75em; + font-weight: bold; + line-height: .6em; + margin-left: -.6em; + color: #7a2518; + text-shadow: 0 1px 2px rgba(0, 0, 0, .1) +} + +.quoteblock blockquote > .paragraph:last-child p { + margin-bottom: 0 +} + +.quoteblock .attribution { + margin-top: .5em; + margin-right: .5ex; + text-align: right +} + +.quoteblock .quoteblock { + margin-left: 0; + margin-right: 0; + padding: .5em 0; + border-left: 3px solid rgba(0, 0, 0, .6) +} + +.quoteblock .quoteblock blockquote { + padding: 0 0 0 .75em +} + +.quoteblock .quoteblock blockquote:before { + display: none +} + +.verseblock { + margin: 0 1em 1.25em 1em +} + +.verseblock pre { + font-family: "Open Sans", "DejaVu Sans", sans; + font-size: 1.15rem; + color: rgba(0, 0, 0, .85); + font-weight: 300; + text-rendering: optimizeLegibility +} + +.verseblock pre strong { + font-weight: 400 +} + +.verseblock .attribution { + margin-top: 1.25rem; + margin-left: .5ex +} + +.quoteblock .attribution, .verseblock .attribution { + font-size: .9375em; + line-height: 1.45; + font-style: italic +} + +.quoteblock .attribution br, .verseblock .attribution br { + display: none +} + +.quoteblock .attribution cite, .verseblock .attribution cite { + display: block; + letter-spacing: -.05em; + color: rgba(0, 0, 0, .6) +} + +.quoteblock.abstract { + margin: 0 0 1.25em 0; + display: block +} + +.quoteblock.abstract blockquote, .quoteblock.abstract blockquote p { + text-align: left; + word-spacing: 0 +} + +.quoteblock.abstract blockquote:before, .quoteblock.abstract blockquote p:first-of-type:before { + display: none +} + +table.tableblock { + max-width: 100%; + border-collapse: separate +} + +table.tableblock td > .paragraph:last-child p > p:last-child, table.tableblock th > p:last-child, table.tableblock td > p:last-child { + margin-bottom: 0 +} + +table.spread { + width: 100% +} + +table.tableblock, th.tableblock, td.tableblock { + border: 0 solid #dedede +} + +table.grid-all th.tableblock, table.grid-all td.tableblock { + border-width: 0 1px 1px 0 +} + +table.grid-all tfoot > tr > th.tableblock, table.grid-all tfoot > tr > td.tableblock { + border-width: 1px 1px 0 0 +} + +table.grid-cols th.tableblock, table.grid-cols td.tableblock { + border-width: 0 1px 0 0 +} + +table.grid-all * > tr > .tableblock:last-child, table.grid-cols * > tr > .tableblock:last-child { + border-right-width: 0 +} + +table.grid-rows th.tableblock, table.grid-rows td.tableblock { + border-width: 0 0 1px 0 +} + +table.grid-all tbody > tr:last-child > th.tableblock, table.grid-all tbody > tr:last-child > td.tableblock, table.grid-all thead:last-child > tr > th.tableblock, table.grid-rows tbody > tr:last-child > th.tableblock, table.grid-rows tbody > tr:last-child > td.tableblock, table.grid-rows thead:last-child > tr > th.tableblock { + border-bottom-width: 0 +} + +table.grid-rows tfoot > tr > th.tableblock, table.grid-rows tfoot > tr > td.tableblock { + border-width: 1px 0 0 0 +} + +table.frame-all { + border-width: 1px +} + +table.frame-sides { + border-width: 0 1px +} + +table.frame-topbot { + border-width: 1px 0 +} + +th.halign-left, td.halign-left { + text-align: left +} + +th.halign-right, td.halign-right { + text-align: right +} + +th.halign-center, td.halign-center { + text-align: center +} + +th.valign-top, td.valign-top { + vertical-align: top +} + +th.valign-bottom, td.valign-bottom { + vertical-align: bottom +} + +th.valign-middle, td.valign-middle { + vertical-align: middle +} + +table thead th, table tfoot th { + font-weight: bold +} + +tbody tr th { + display: table-cell; + line-height: 1.6; + background: #f7f8f7 +} + +tbody tr th, tbody tr th p, tfoot tr th, tfoot tr th p { + color: rgba(0, 0, 0, .8); + font-weight: bold +} + +p.tableblock > code:only-child { + background: none; + padding: 0 +} + +p.tableblock { + font-size: 1em +} + +td > div.verse { + white-space: pre +} + +ol { + margin-left: 1.75em +} + +ul li ol { + margin-left: 1.5em +} + +dl dd { + margin-left: 1.125em +} + +dl dd:last-child, dl dd:last-child > :last-child { + margin-bottom: 0 +} + +ol > li p, ul > li p, ul dd, ol dd, .olist .olist, .ulist .ulist, .ulist .olist, .olist .ulist { + margin-bottom: .625em +} + +ul.unstyled, ol.unnumbered, ul.checklist, ul.none { + list-style-type: none +} + +ul.unstyled, ol.unnumbered, ul.checklist { + margin-left: .625em +} + +ul.checklist li > p:first-child > .fa-check-square-o:first-child, ul.checklist li > p:first-child > input[type="checkbox"]:first-child { + margin-right: .25em +} + +ul.checklist li > p:first-child > input[type="checkbox"]:first-child { + position: relative; + top: 1px +} + +ul.inline { + margin: 0 auto .625em auto; + margin-left: -1.375em; + margin-right: 0; + padding: 0; + list-style: none; + overflow: hidden +} + +ul.inline > li { + list-style: none; + float: left; + margin-left: 1.375em; + display: block +} + +ul.inline > li > * { + display: block +} + +.unstyled dl dt { + font-weight: 400; + font-style: normal +} + +ol.arabic { + list-style-type: decimal +} + +ol.decimal { + list-style-type: decimal-leading-zero +} + +ol.loweralpha { + list-style-type: lower-alpha +} + +ol.upperalpha { + list-style-type: upper-alpha +} + +ol.lowerroman { + list-style-type: lower-roman +} + +ol.upperroman { + list-style-type: upper-roman +} + +ol.lowergreek { + list-style-type: lower-greek +} + +.hdlist > table, .colist > table { + border: 0; + background: none +} + +.hdlist > table > tbody > tr, .colist > table > tbody > tr { + background: none +} + +td.hdlist1 { + padding-right: .75em; + font-weight: bold +} + +td.hdlist1, td.hdlist2 { + vertical-align: top +} + +.literalblock + .colist, .listingblock + .colist { + margin-top: -.5em +} + +.colist > table tr > td:first-of-type { + padding: 0 .75em; + line-height: 1 +} + +.colist > table tr > td:last-of-type { + padding: .25em 0 +} + +.thumb, .th { + line-height: 0; + display: inline-block; + border: solid 4px #fff; + -webkit-box-shadow: 0 0 0 1px #ddd; + box-shadow: 0 0 0 1px #ddd +} + +.imageblock.left, .imageblock[style*="float: left"] { + margin: .25em .625em 1.25em 0 +} + +.imageblock.right, .imageblock[style*="float: right"] { + margin: .25em 0 1.25em .625em +} + +.imageblock > .title { + margin-bottom: 0 +} + +.imageblock.thumb, .imageblock.th { + border-width: 6px +} + +.imageblock.thumb > .title, .imageblock.th > .title { + padding: 0 .125em +} + +.image.left, .image.right { + margin-top: .25em; + margin-bottom: .25em; + display: inline-block; + line-height: 0 +} + +.image.left { + margin-right: .625em +} + +.image.right { + margin-left: .625em +} + +a.image { + text-decoration: none +} + +span.footnote, span.footnoteref { + vertical-align: super; + font-size: .875em +} + +span.footnote a, span.footnoteref a { + text-decoration: none +} + +span.footnote a:active, span.footnoteref a:active { + text-decoration: underline +} + +#footnotes { + padding-top: .75em; + padding-bottom: .75em; + margin-bottom: .625em +} + +#footnotes hr { + width: 20%; + min-width: 6.25em; + margin: -.25em 0 .75em 0; + border-width: 1px 0 0 0 +} + +#footnotes .footnote { + padding: 0 .375em; + line-height: 1.3; + font-size: .875em; + margin-left: 1.2em; + text-indent: -1.2em; + margin-bottom: .2em +} + +#footnotes .footnote a:first-of-type { + font-weight: bold; + text-decoration: none +} + +#footnotes .footnote:last-of-type { + margin-bottom: 0 +} + +#content #footnotes { + margin-top: -.625em; + margin-bottom: 0; + padding: .75em 0 +} + +.gist .file-data > table { + border: 0; + background: #fff; + width: 100%; + margin-bottom: 0 +} + +.gist .file-data > table td.line-data { + width: 99% +} + +div.unbreakable { + page-break-inside: avoid +} + +.big { + font-size: larger +} + +.small { + font-size: smaller +} + +.underline { + text-decoration: underline +} + +.overline { + text-decoration: overline +} + +.line-through { + text-decoration: line-through +} + +.aqua { + color: #00bfbf +} + +.aqua-background { + background-color: #00fafa +} + +.black { + color: #000 +} + +.black-background { + background-color: #000 +} + +.blue { + color: #0000bf +} + +.blue-background { + background-color: #0000fa +} + +.fuchsia { + color: #bf00bf +} + +.fuchsia-background { + background-color: #fa00fa +} + +.gray { + color: #606060 +} + +.gray-background { + background-color: #7d7d7d +} + +.green { + color: #006000 +} + +.green-background { + background-color: #007d00 +} + +.lime { + color: #00bf00 +} + +.lime-background { + background-color: #00fa00 +} + +.maroon { + color: #600000 +} + +.maroon-background { + background-color: #7d0000 +} + +.navy { + color: #000060 +} + +.navy-background { + background-color: #00007d +} + +.olive { + color: #606000 +} + +.olive-background { + background-color: #7d7d00 +} + +.purple { + color: #600060 +} + +.purple-background { + background-color: #7d007d +} + +.red { + color: #bf0000 +} + +.red-background { + background-color: #fa0000 +} + +.silver { + color: #909090 +} + +.silver-background { + background-color: #bcbcbc +} + +.teal { + color: #006060 +} + +.teal-background { + background-color: #007d7d +} + +.white { + color: #bfbfbf +} + +.white-background { + background-color: #fafafa +} + +.yellow { + color: #bfbf00 +} + +.yellow-background { + background-color: #fafa00 +} + +span.icon > .fa { + cursor: default +} + +.admonitionblock td.icon [class^="fa icon-"] { + font-size: 2.5em; + text-shadow: 1px 1px 2px rgba(0, 0, 0, .5); + cursor: default +} + +.admonitionblock td.icon .icon-note:before { + content: "\f05a"; + color: #19407c +} + +.admonitionblock td.icon .icon-tip:before { + content: "\f0eb"; + text-shadow: 1px 1px 2px rgba(155, 155, 0, .8); + color: #111 +} + +.admonitionblock td.icon .icon-warning:before { + content: "\f071"; + color: #bf6900 +} + +.admonitionblock td.icon .icon-caution:before { + content: "\f06d"; + color: #bf3400 +} + +.admonitionblock td.icon .icon-important:before { + content: "\f06a"; + color: #bf0000 +} + +.conum[data-value] { + display: inline-block; + color: #fff !important; + background-color: rgba(0, 0, 0, .8); + -webkit-border-radius: 100px; + border-radius: 100px; + text-align: center; + font-size: .75em; + width: 1.67em; + height: 1.67em; + line-height: 1.67em; + font-family: "Open Sans", "DejaVu Sans", sans-serif; + font-style: normal; + font-weight: bold +} + +.conum[data-value] * { + color: #fff !important +} + +.conum[data-value] + b { + display: none +} + +.conum[data-value]:after { + content: attr(data-value) +} + +pre .conum[data-value] { + position: relative; + top: -.125em +} + +b.conum * { + color: inherit !important +} + +.conum:not([data-value]):empty { + display: none +} + +h1, h2 { + letter-spacing: -.01em +} + +dt, th.tableblock, td.content { + text-rendering: optimizeLegibility +} + +p, td.content { + letter-spacing: -.01em +} + +p strong, td.content strong { + letter-spacing: -.005em +} + +p, blockquote, dt, td.content { + font-size: 1.0625rem +} + +p { + margin-bottom: 1.25rem +} + +.sidebarblock p, .sidebarblock dt, .sidebarblock td.content, p.tableblock { + font-size: 1em +} + +.exampleblock > .content { + background-color: #fffef7; + border-color: #e0e0dc; + -webkit-box-shadow: 0 1px 4px #e0e0dc; + box-shadow: 0 1px 4px #e0e0dc +} + +.print-only { + display: none !important +} + +@media print { + @page { + margin: 1.25cm .75cm + } + + * { + -webkit-box-shadow: none !important; + box-shadow: none !important; + text-shadow: none !important + } + + a { + color: inherit !important; + text-decoration: underline !important + } + + a.bare, a[href^="#"], a[href^="mailto:"] { + text-decoration: none !important + } + + a[href^="http:"]:not(.bare):after, a[href^="https:"]:not(.bare):after { + content: "(" attr(href) ")"; + display: inline-block; + font-size: .875em; + padding-left: .25em + } + + abbr[title]:after { + content: " (" attr(title) ")" + } + + pre, blockquote, tr, img { + page-break-inside: avoid + } + + thead { + display: table-header-group + } + + img { + max-width: 100% !important + } + + p, blockquote, dt, td.content { + font-size: 1em; + orphans: 3; + widows: 3 + } + + h2, h3, #toctitle, .sidebarblock > .content > .title { + page-break-after: avoid + } + + #toc, .sidebarblock, .exampleblock > .content { + background: none !important + } + + #toc { + border-bottom: 1px solid #ddddd8 !important; + padding-bottom: 0 !important + } + + .sect1 { + padding-bottom: 0 !important + } + + .sect1 + .sect1 { + border: 0 !important + } + + #header > h1:first-child { + margin-top: 1.25rem + } + + body.book #header { + text-align: center + } + + body.book #header > h1:first-child { + border: 0 !important; + margin: 2.5em 0 1em 0 + } + + body.book #header .details { + border: 0 !important; + display: block; + padding: 0 !important + } + + body.book #header .details span:first-child { + margin-left: 0 !important + } + + body.book #header .details br { + display: block + } + + body.book #header .details br + span:before { + content: none !important + } + + body.book #toc { + border: 0 !important; + text-align: left !important; + padding: 0 !important; + margin: 0 !important + } + + body.book #toc, body.book #preamble, body.book h1.sect0, body.book .sect1 > h2 { + page-break-before: always + } + + .listingblock code[data-lang]:before { + display: block + } + + #footer { + background: none !important; + padding: 0 .9375em + } + + #footer-text { + color: rgba(0, 0, 0, .6) !important; + font-size: .9em + } + + .hide-on-print { + display: none !important + } + + .print-only { + display: block !important + } + + .hide-for-print { + display: none !important + } + + .show-for-print { + display: inherit !important + } +} diff --git a/docs/pom.xml b/docs/pom.xml index 5ce54fd8..c691841e 100644 --- a/docs/pom.xml +++ b/docs/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 org.springframework.cloud diff --git a/pom.xml b/pom.xml index a2b87506..0b6511ef 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,7 @@ - + 4.0.0 spring-cloud-openfeign 2.1.1.BUILD-SNAPSHOT @@ -11,12 +12,15 @@ org.springframework.cloud spring-cloud-build 2.1.3.BUILD-SNAPSHOT - + https://github.com/spring-cloud/spring-cloud-openfeign - scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git - scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git + scm:git:git://github.com/spring-cloud/spring-cloud-openfeign.git + + + scm:git:ssh://git@github.com/spring-cloud/spring-cloud-openfeign.git + HEAD @@ -32,8 +36,15 @@ 2.19.1 jacoco reuseReports - ${project.basedir}/../target/jacoco.exec + ${project.basedir}/../target/jacoco.exec + java + + true + true + + true + @@ -46,11 +57,13 @@ .settings/org.eclipse.jdt.ui.prefs - ${main.basedir}/eclipse/org.eclipse.jdt.ui.prefs + ${main.basedir}/eclipse/org.eclipse.jdt.ui.prefs + .settings/org.eclipse.jdt.core.prefs - ${main.basedir}/eclipse/org.eclipse.jdt.core.prefs + ${main.basedir}/eclipse/org.eclipse.jdt.core.prefs + @@ -63,8 +76,26 @@ 1.8 + + org.apache.maven.plugins + maven-checkstyle-plugin + + + io.spring.javaformat + spring-javaformat-maven-plugin + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + + @@ -106,7 +137,7 @@ spring-cloud-openfeign-core spring-cloud-starter-openfeign docs - + spring @@ -184,7 +215,8 @@ surefireArgLine - ${project.build.directory}/jacoco.exec + ${project.build.directory}/jacoco.exec + @@ -195,7 +227,8 @@ - ${project.build.directory}/jacoco.exec + ${project.build.directory}/jacoco.exec + diff --git a/spring-cloud-openfeign-core/pom.xml b/spring-cloud-openfeign-core/pom.xml index 436171b0..2d10c85d 100644 --- a/spring-cloud-openfeign-core/pom.xml +++ b/spring-cloud-openfeign-core/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 @@ -218,18 +219,18 @@ java8plus - - [1.8,2.0) - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - -parameters - + + [1.8,2.0) + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + -parameters + diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/AnnotatedParameterProcessor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/AnnotatedParameterProcessor.java index e8537fd4..bf04e7bf 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/AnnotatedParameterProcessor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/AnnotatedParameterProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,20 +32,19 @@ public interface AnnotatedParameterProcessor { /** * Retrieves the processor supported annotation type. - * * @return the annotation type */ Class getAnnotationType(); /** * Process the annotated parameter. - * - * @param context the parameter context + * @param context the parameter context * @param annotation the annotation instance - * @param method the method that contains the annotation + * @param method the method that contains the annotation * @return whether the parameter is http */ - boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method); + boolean processArgument(AnnotatedParameterContext context, Annotation annotation, + Method method); /** * Specifies the parameter context. @@ -56,32 +55,30 @@ public interface AnnotatedParameterProcessor { /** * Retrieves the method metadata. - * * @return the method metadata */ MethodMetadata getMethodMetadata(); /** * Retrieves the index of the parameter. - * * @return the parameter index */ int getParameterIndex(); /** * Sets the parameter name. - * * @param name the name of the parameter */ void setParameterName(String name); /** * Sets the template parameter. - * * @param name the template parameter * @param rest the existing parameter values * @return parameters */ Collection setTemplateParameter(String name, Collection rest); + } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultFeignLoggerFactory.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultFeignLoggerFactory.java index 4950a1ab..43cf43a0 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultFeignLoggerFactory.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultFeignLoggerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultTargeter.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultTargeter.java index 67666cec..a7a24d0c 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultTargeter.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/DefaultTargeter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; @@ -26,8 +25,9 @@ import feign.Target; class DefaultTargeter implements Targeter { @Override - public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, - Target.HardCodedTarget target) { + public T target(FeignClientFactoryBean factory, Feign.Builder feign, + FeignContext context, Target.HardCodedTarget target) { return feign.target(target); } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/EnableFeignClients.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/EnableFeignClients.java index dfed4f63..0c86415b 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/EnableFeignClients.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/EnableFeignClients.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,7 +55,6 @@ public @interface EnableFeignClients { *

* Use {@link #basePackageClasses()} for a type-safe alternative to String-based * package names. - * * @return the array of 'basePackages'. */ String[] basePackages() default {}; @@ -66,7 +65,6 @@ public @interface EnableFeignClients { *

* Consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. - * * @return the array of 'basePackageClasses'. */ Class[] basePackageClasses() default {}; @@ -77,12 +75,15 @@ public @interface EnableFeignClients { * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}. * * @see FeignClientsConfiguration for the defaults + * @return list of default configurations */ Class[] defaultConfiguration() default {}; /** - * List of classes annotated with @FeignClient. If not empty, disables classpath scanning. + * List of classes annotated with @FeignClient. If not empty, disables classpath + * scanning. * @return list of FeignClient classes */ Class[] clients() default {}; + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java index 8b510e95..6e71b28f 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,11 +22,19 @@ import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.TimeUnit; +import javax.annotation.PreDestroy; + +import feign.Client; +import feign.Feign; +import feign.httpclient.ApacheHttpClient; +import feign.okhttp.OkHttpClient; +import okhttp3.ConnectionPool; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.CloseableHttpClient; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; @@ -42,21 +50,14 @@ import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import feign.Client; -import feign.Feign; -import feign.httpclient.ApacheHttpClient; -import feign.okhttp.OkHttpClient; -import okhttp3.ConnectionPool; - -import javax.annotation.PreDestroy; - /** * @author Spencer Gibb * @author Julien Roy */ @Configuration @ConditionalOnClass(Feign.class) -@EnableConfigurationProperties({FeignClientProperties.class, FeignHttpClientProperties.class}) +@EnableConfigurationProperties({ FeignClientProperties.class, + FeignHttpClientProperties.class }) public class FeignAutoConfiguration { @Autowired(required = false) @@ -77,21 +78,25 @@ public class FeignAutoConfiguration { @Configuration @ConditionalOnClass(name = "feign.hystrix.HystrixFeign") protected static class HystrixFeignTargeterConfiguration { + @Bean @ConditionalOnMissingBean public Targeter feignTargeter() { return new HystrixTargeter(); } + } @Configuration @ConditionalOnMissingClass("feign.hystrix.HystrixFeign") protected static class DefaultFeignTargeterConfiguration { + @Bean @ConditionalOnMissingBean public Targeter feignTargeter() { return new DefaultTargeter(); } + } // the following configuration is for alternate feign clients if @@ -104,6 +109,7 @@ public class FeignAutoConfiguration { @ConditionalOnMissingBean(CloseableHttpClient.class) @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true) protected static class HttpClientFeignConfiguration { + private final Timer connectionManagerTimer = new Timer( "FeignApacheHttpClientConfiguration.connectionManagerTimer", true); @@ -118,10 +124,12 @@ public class FeignAutoConfiguration { ApacheHttpClientConnectionManagerFactory connectionManagerFactory, FeignHttpClientProperties httpClientProperties) { final HttpClientConnectionManager connectionManager = connectionManagerFactory - .newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(), + .newConnectionManager(httpClientProperties.isDisableSslValidation(), + httpClientProperties.getMaxConnections(), httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(), - httpClientProperties.getTimeToLiveUnit(), registryBuilder); + httpClientProperties.getTimeToLiveUnit(), + this.registryBuilder); this.connectionManagerTimer.schedule(new TimerTask() { @Override public void run() { @@ -139,9 +147,9 @@ public class FeignAutoConfiguration { .setConnectTimeout(httpClientProperties.getConnectionTimeout()) .setRedirectsEnabled(httpClientProperties.isFollowRedirects()) .build(); - this.httpClient = httpClientFactory.createBuilder(). - setConnectionManager(httpClientConnectionManager). - setDefaultRequestConfig(defaultRequestConfig).build(); + this.httpClient = httpClientFactory.createBuilder() + .setConnectionManager(httpClientConnectionManager) + .setDefaultRequestConfig(defaultRequestConfig).build(); return this.httpClient; } @@ -153,26 +161,28 @@ public class FeignAutoConfiguration { @PreDestroy public void destroy() throws Exception { - connectionManagerTimer.cancel(); - if(httpClient != null) { - httpClient.close(); + this.connectionManagerTimer.cancel(); + if (this.httpClient != null) { + this.httpClient.close(); } } + } @Configuration @ConditionalOnClass(OkHttpClient.class) @ConditionalOnMissingClass("com.netflix.loadbalancer.ILoadBalancer") @ConditionalOnMissingBean(okhttp3.OkHttpClient.class) - @ConditionalOnProperty(value = "feign.okhttp.enabled") + @ConditionalOnProperty("feign.okhttp.enabled") protected static class OkHttpFeignConfiguration { private okhttp3.OkHttpClient okHttpClient; @Bean @ConditionalOnMissingBean(ConnectionPool.class) - public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, - OkHttpClientConnectionPoolFactory connectionPoolFactory) { + public ConnectionPool httpClientConnectionPool( + FeignHttpClientProperties httpClientProperties, + OkHttpClientConnectionPoolFactory connectionPoolFactory) { Integer maxTotalConnections = httpClientProperties.getMaxConnections(); Long timeToLive = httpClientProperties.getTimeToLive(); TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit(); @@ -181,22 +191,23 @@ public class FeignAutoConfiguration { @Bean public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, - ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) { + ConnectionPool connectionPool, + FeignHttpClientProperties httpClientProperties) { Boolean followRedirects = httpClientProperties.isFollowRedirects(); Integer connectTimeout = httpClientProperties.getConnectionTimeout(); Boolean disableSslValidation = httpClientProperties.isDisableSslValidation(); - this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation). - connectTimeout(connectTimeout, TimeUnit.MILLISECONDS). - followRedirects(followRedirects). - connectionPool(connectionPool).build(); + this.okHttpClient = httpClientFactory.createBuilder(disableSslValidation) + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) + .followRedirects(followRedirects).connectionPool(connectionPool) + .build(); return this.okHttpClient; } @PreDestroy public void destroy() { - if(okHttpClient != null) { - okHttpClient.dispatcher().executorService().shutdown(); - okHttpClient.connectionPool().evictAll(); + if (this.okHttpClient != null) { + this.okHttpClient.dispatcher().executorService().shutdown(); + this.okHttpClient.connectionPool().evictAll(); } } @@ -205,6 +216,7 @@ public class FeignAutoConfiguration { public Client feignClient(okhttp3.OkHttpClient client) { return new OkHttpClient(client); } + } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClient.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClient.java index 17c63bed..a54c5649 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClient.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -42,41 +42,45 @@ public @interface FeignClient { * The name of the service with optional protocol prefix. Synonym for {@link #name() * name}. A name must be specified for all clients, whether or not a url is provided. * Can be specified as property key, eg: ${propertyKey}. + * @return the name of the service with optional protocol prefix */ @AliasFor("name") String value() default ""; /** * The service id with optional protocol prefix. Synonym for {@link #value() value}. - * * @deprecated use {@link #name() name} instead + * @return the service id with optional protocol prefix */ @Deprecated String serviceId() default ""; /** - * This will be used as the bean name instead of name if present, but will not be used as a service id. + * This will be used as the bean name instead of name if present, but will not be used + * as a service id. + * @return bean name instead of name if present */ String contextId() default ""; /** - * The service id with optional protocol prefix. Synonym for {@link #value() value}. + * @return The service id with optional protocol prefix. Synonym for {@link #value() + * value}. */ @AliasFor("value") String name() default ""; - + /** - * Sets the @Qualifier value for the feign client. + * @return the @Qualifier value for the feign client. */ String qualifier() default ""; /** - * An absolute URL or resolvable hostname (the protocol is optional). + * @return an absolute URL or resolvable hostname (the protocol is optional). */ String url() default ""; /** - * Whether 404s should be decoded instead of throwing FeignExceptions + * @return whether 404s should be decoded instead of throwing FeignExceptions */ boolean decode404() default false; @@ -86,33 +90,35 @@ public @interface FeignClient { * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}. * * @see FeignClientsConfiguration for the defaults + * @return list of configurations for feign client */ Class[] configuration() default {}; /** * Fallback class for the specified Feign client interface. The fallback class must * implement the interface annotated by this annotation and be a valid spring bean. + * @return fallback class for the specified Feign client interface */ Class fallback() default void.class; /** * Define a fallback factory for the specified Feign client interface. The fallback * factory must produce instances of fallback classes that implement the interface - * annotated by {@link FeignClient}. The fallback factory must be a valid spring - * bean. + * annotated by {@link FeignClient}. The fallback factory must be a valid spring bean. * * @see feign.hystrix.FallbackFactory for details. + * @return fallback factory for the specified Feign client interface */ Class fallbackFactory() default void.class; /** - * Path prefix to be used by all method-level mappings. Can be used with or without - * @RibbonClient. + * @return path prefix to be used by all method-level mappings. Can be used with or + * without @RibbonClient. */ String path() default ""; /** - * Whether to mark the feign proxy as a primary bean. Defaults to true. + * @return whether to mark the feign proxy as a primary bean. Defaults to true. */ boolean primary() default true; diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java index 626a8284..4a1ce6cc 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,10 +35,15 @@ public class FeignClientBuilder { } public Builder forType(final Class type, final String name) { - return new Builder<>(applicationContext, type, name); + return new Builder<>(this.applicationContext, type, name); } - public static class Builder { + /** + * Builder of feign targets. + * + * @param type of target + */ + public static final class Builder { private FeignClientFactoryBean feignClientFactoryBean; @@ -95,5 +100,7 @@ public class FeignClientBuilder { public T build() { return this.feignClientFactoryBean.getTarget(); } + } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java index 2b0f1b08..d53cca82 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,17 +19,6 @@ package org.springframework.cloud.openfeign; import java.util.Map; import java.util.Objects; -import org.springframework.beans.BeanUtils; -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.FactoryBean; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; -import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; -import org.springframework.util.Assert; -import org.springframework.util.StringUtils; - import feign.Client; import feign.Contract; import feign.Feign; @@ -42,16 +31,29 @@ import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; +import org.springframework.beans.BeanUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.util.Assert; +import org.springframework.util.StringUtils; + /** * @author Spencer Gibb * @author Venil Noronha * @author Eko Kurniawan Khannedy * @author Gregor Zurowski */ -class FeignClientFactoryBean implements FactoryBean, InitializingBean, - ApplicationContextAware { +class FeignClientFactoryBean + implements FactoryBean, InitializingBean, ApplicationContextAware { + /*********************************** - * WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some lifecycle race condition. + * WARNING! Nothing in this class should be @Autowired. It causes NPEs because of some + * lifecycle race condition. ***********************************/ private Class type; @@ -78,11 +80,6 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, Assert.hasText(this.name, "Name must be set"); } - @Override - public void setApplicationContext(ApplicationContext context) throws BeansException { - this.applicationContext = context; - } - protected Feign.Builder feign(FeignContext context) { FeignLoggerFactory loggerFactory = get(context, FeignLoggerFactory.class); Logger logger = loggerFactory.create(this.type); @@ -102,23 +99,33 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } protected void configureFeign(FeignContext context, Feign.Builder builder) { - FeignClientProperties properties = applicationContext.getBean(FeignClientProperties.class); + FeignClientProperties properties = this.applicationContext + .getBean(FeignClientProperties.class); if (properties != null) { if (properties.isDefaultToProperties()) { configureUsingConfiguration(context, builder); - configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); - configureUsingProperties(properties.getConfig().get(this.contextId), builder); - } else { - configureUsingProperties(properties.getConfig().get(properties.getDefaultConfig()), builder); - configureUsingProperties(properties.getConfig().get(this.contextId), builder); + configureUsingProperties( + properties.getConfig().get(properties.getDefaultConfig()), + builder); + configureUsingProperties(properties.getConfig().get(this.contextId), + builder); + } + else { + configureUsingProperties( + properties.getConfig().get(properties.getDefaultConfig()), + builder); + configureUsingProperties(properties.getConfig().get(this.contextId), + builder); configureUsingConfiguration(context, builder); } - } else { + } + else { configureUsingConfiguration(context, builder); } } - protected void configureUsingConfiguration(FeignContext context, Feign.Builder builder) { + protected void configureUsingConfiguration(FeignContext context, + Feign.Builder builder) { Logger.Level level = getOptional(context, Logger.Level.class); if (level != null) { builder.logLevel(level); @@ -135,18 +142,20 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, if (options != null) { builder.options(options); } - Map requestInterceptors = context.getInstances( - this.contextId, RequestInterceptor.class); + Map requestInterceptors = context + .getInstances(this.contextId, RequestInterceptor.class); if (requestInterceptors != null) { builder.requestInterceptors(requestInterceptors.values()); } - if (decode404) { + if (this.decode404) { builder.decode404(); } } - protected void configureUsingProperties(FeignClientProperties.FeignClientConfiguration config, Feign.Builder builder) { + protected void configureUsingProperties( + FeignClientProperties.FeignClientConfiguration config, + Feign.Builder builder) { if (config == null) { return; } @@ -156,7 +165,8 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } if (config.getConnectTimeout() != null && config.getReadTimeout() != null) { - builder.options(new Request.Options(config.getConnectTimeout(), config.getReadTimeout())); + builder.options(new Request.Options(config.getConnectTimeout(), + config.getReadTimeout())); } if (config.getRetryer() != null) { @@ -169,7 +179,8 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, builder.errorDecoder(errorDecoder); } - if (config.getRequestInterceptors() != null && !config.getRequestInterceptors().isEmpty()) { + if (config.getRequestInterceptors() != null + && !config.getRequestInterceptors().isEmpty()) { // this will add request interceptor to builder, not replace existing for (Class bean : config.getRequestInterceptors()) { RequestInterceptor interceptor = getOrInstantiate(bean); @@ -198,8 +209,9 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, private T getOrInstantiate(Class tClass) { try { - return applicationContext.getBean(tClass); - } catch (NoSuchBeanDefinitionException e) { + return this.applicationContext.getBean(tClass); + } + catch (NoSuchBeanDefinitionException e) { return BeanUtils.instantiateClass(tClass); } } @@ -207,8 +219,8 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, protected T get(FeignContext context, Class type) { T instance = context.getInstance(this.contextId, type); if (instance == null) { - throw new IllegalStateException("No bean found of type " + type + " for " - + this.contextId); + throw new IllegalStateException( + "No bean found of type " + type + " for " + this.contextId); } return instance; } @@ -237,22 +249,23 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, /** * @param the target type of the Feign client - * @return a {@link Feign} client created with the specified data and the context information + * @return a {@link Feign} client created with the specified data and the context + * information */ T getTarget() { - FeignContext context = applicationContext.getBean(FeignContext.class); + FeignContext context = this.applicationContext.getBean(FeignContext.class); Feign.Builder builder = feign(context); if (!StringUtils.hasText(this.url)) { if (!this.name.startsWith("http")) { - url = "http://" + this.name; + this.url = "http://" + this.name; } else { - url = this.name; + this.url = this.name; } - url += cleanPath(); - return (T) loadBalance(builder, context, new HardCodedTarget<>(this.type, - this.name, url)); + this.url += cleanPath(); + return (T) loadBalance(builder, context, + new HardCodedTarget<>(this.type, this.name, this.url)); } if (StringUtils.hasText(this.url) && !this.url.startsWith("http")) { this.url = "http://" + this.url; @@ -263,13 +276,13 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, if (client instanceof LoadBalancerFeignClient) { // not load balancing because we have a url, // but ribbon is on the classpath, so unwrap - client = ((LoadBalancerFeignClient)client).getDelegate(); + client = ((LoadBalancerFeignClient) client).getDelegate(); } builder.client(client); } Targeter targeter = get(context, Targeter.class); - return (T) targeter.target(this, builder, context, new HardCodedTarget<>( - this.type, this.name, url)); + return (T) targeter.target(this, builder, context, + new HardCodedTarget<>(this.type, this.name, url)); } private String cleanPath() { @@ -296,7 +309,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } public Class getType() { - return type; + return this.type; } public void setType(Class type) { @@ -304,7 +317,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } public String getName() { - return name; + return this.name; } public void setName(String name) { @@ -312,7 +325,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } public String getContextId() { - return contextId; + return this.contextId; } public void setContextId(String contextId) { @@ -320,7 +333,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } public String getUrl() { - return url; + return this.url; } public void setUrl(String url) { @@ -328,7 +341,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } public String getPath() { - return path; + return this.path; } public void setPath(String path) { @@ -336,7 +349,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } public boolean isDecode404() { - return decode404; + return this.decode404; } public void setDecode404(boolean decode404) { @@ -344,11 +357,16 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } public ApplicationContext getApplicationContext() { - return applicationContext; + return this.applicationContext; + } + + @Override + public void setApplicationContext(ApplicationContext context) throws BeansException { + this.applicationContext = context; } public Class getFallback() { - return fallback; + return this.fallback; } public void setFallback(Class fallback) { @@ -356,7 +374,7 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, } public Class getFallbackFactory() { - return fallbackFactory; + return this.fallbackFactory; } public void setFallbackFactory(Class fallbackFactory) { @@ -365,37 +383,39 @@ class FeignClientFactoryBean implements FactoryBean, InitializingBean, @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } FeignClientFactoryBean that = (FeignClientFactoryBean) o; - return Objects.equals(applicationContext, that.applicationContext) && - decode404 == that.decode404 && - Objects.equals(fallback, that.fallback) && - Objects.equals(fallbackFactory, that.fallbackFactory) && - Objects.equals(name, that.name) && - Objects.equals(path, that.path) && - Objects.equals(type, that.type) && - Objects.equals(url, that.url); + return Objects.equals(this.applicationContext, that.applicationContext) + && this.decode404 == that.decode404 + && Objects.equals(this.fallback, that.fallback) + && Objects.equals(this.fallbackFactory, that.fallbackFactory) + && Objects.equals(this.name, that.name) + && Objects.equals(this.path, that.path) + && Objects.equals(this.type, that.type) + && Objects.equals(this.url, that.url); } @Override public int hashCode() { - return Objects.hash(applicationContext, decode404, fallback, fallbackFactory, - name, path, type, url); + return Objects.hash(this.applicationContext, this.decode404, this.fallback, + this.fallbackFactory, this.name, this.path, this.type, this.url); } @Override public String toString() { - return new StringBuilder("FeignClientFactoryBean{") - .append("type=").append(type).append(", ") - .append("name='").append(name).append("', ") - .append("url='").append(url).append("', ") - .append("path='").append(path).append("', ") - .append("decode404=").append(decode404).append(", ") - .append("applicationContext=").append(applicationContext).append(", ") - .append("fallback=").append(fallback).append(", ") - .append("fallbackFactory=").append(fallbackFactory) - .append("}").toString(); + return new StringBuilder("FeignClientFactoryBean{").append("type=") + .append(this.type).append(", ").append("name='").append(this.name) + .append("', ").append("url='").append(this.url).append("', ") + .append("path='").append(this.path).append("', ").append("decode404=") + .append(this.decode404).append(", ").append("applicationContext=") + .append(this.applicationContext).append(", ").append("fallback=") + .append(this.fallback).append(", ").append("fallbackFactory=") + .append(this.fallbackFactory).append("}").toString(); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java index 2483caad..a98049a1 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,8 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.cloud.openfeign; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; + import feign.Contract; import feign.Logger; import feign.RequestInterceptor; @@ -22,12 +28,8 @@ import feign.Retryer; import feign.codec.Decoder; import feign.codec.Encoder; import feign.codec.ErrorDecoder; -import org.springframework.boot.context.properties.ConfigurationProperties; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; +import org.springframework.boot.context.properties.ConfigurationProperties; /** * @author Eko Kurniawan Khannedy @@ -42,7 +44,7 @@ public class FeignClientProperties { private Map config = new HashMap<>(); public boolean isDefaultToProperties() { - return defaultToProperties; + return this.defaultToProperties; } public void setDefaultToProperties(boolean defaultToProperties) { @@ -50,7 +52,7 @@ public class FeignClientProperties { } public String getDefaultConfig() { - return defaultConfig; + return this.defaultConfig; } public void setDefaultConfig(String defaultConfig) { @@ -58,7 +60,7 @@ public class FeignClientProperties { } public Map getConfig() { - return config; + return this.config; } public void setConfig(Map config) { @@ -67,19 +69,26 @@ public class FeignClientProperties { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } FeignClientProperties that = (FeignClientProperties) o; - return defaultToProperties == that.defaultToProperties && - Objects.equals(defaultConfig, that.defaultConfig) && - Objects.equals(config, that.config); + return this.defaultToProperties == that.defaultToProperties + && Objects.equals(this.defaultConfig, that.defaultConfig) + && Objects.equals(this.config, that.config); } @Override public int hashCode() { - return Objects.hash(defaultToProperties, defaultConfig, config); + return Objects.hash(this.defaultToProperties, this.defaultConfig, this.config); } + /** + * Feign client configuration. + */ public static class FeignClientConfiguration { private Logger.Level loggerLevel; @@ -103,7 +112,7 @@ public class FeignClientProperties { private Class contract; public Logger.Level getLoggerLevel() { - return loggerLevel; + return this.loggerLevel; } public void setLoggerLevel(Logger.Level loggerLevel) { @@ -111,7 +120,7 @@ public class FeignClientProperties { } public Integer getConnectTimeout() { - return connectTimeout; + return this.connectTimeout; } public void setConnectTimeout(Integer connectTimeout) { @@ -119,7 +128,7 @@ public class FeignClientProperties { } public Integer getReadTimeout() { - return readTimeout; + return this.readTimeout; } public void setReadTimeout(Integer readTimeout) { @@ -127,7 +136,7 @@ public class FeignClientProperties { } public Class getRetryer() { - return retryer; + return this.retryer; } public void setRetryer(Class retryer) { @@ -135,7 +144,7 @@ public class FeignClientProperties { } public Class getErrorDecoder() { - return errorDecoder; + return this.errorDecoder; } public void setErrorDecoder(Class errorDecoder) { @@ -143,15 +152,16 @@ public class FeignClientProperties { } public List> getRequestInterceptors() { - return requestInterceptors; + return this.requestInterceptors; } - public void setRequestInterceptors(List> requestInterceptors) { + public void setRequestInterceptors( + List> requestInterceptors) { this.requestInterceptors = requestInterceptors; } public Boolean getDecode404() { - return decode404; + return this.decode404; } public void setDecode404(Boolean decode404) { @@ -159,7 +169,7 @@ public class FeignClientProperties { } public Class getDecoder() { - return decoder; + return this.decoder; } public void setDecoder(Class decoder) { @@ -167,7 +177,7 @@ public class FeignClientProperties { } public Class getEncoder() { - return encoder; + return this.encoder; } public void setEncoder(Class encoder) { @@ -175,7 +185,7 @@ public class FeignClientProperties { } public Class getContract() { - return contract; + return this.contract; } public void setContract(Class contract) { @@ -184,26 +194,32 @@ public class FeignClientProperties { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } FeignClientConfiguration that = (FeignClientConfiguration) o; - return loggerLevel == that.loggerLevel && - Objects.equals(connectTimeout, that.connectTimeout) && - Objects.equals(readTimeout, that.readTimeout) && - Objects.equals(retryer, that.retryer) && - Objects.equals(errorDecoder, that.errorDecoder) && - Objects.equals(requestInterceptors, that.requestInterceptors) && - Objects.equals(decode404, that.decode404) && - Objects.equals(encoder, that.encoder) && - Objects.equals(decoder, that.decoder) && - Objects.equals(contract, that.contract); + return this.loggerLevel == that.loggerLevel + && Objects.equals(this.connectTimeout, that.connectTimeout) + && Objects.equals(this.readTimeout, that.readTimeout) + && Objects.equals(this.retryer, that.retryer) + && Objects.equals(this.errorDecoder, that.errorDecoder) + && Objects.equals(this.requestInterceptors, that.requestInterceptors) + && Objects.equals(this.decode404, that.decode404) + && Objects.equals(this.encoder, that.encoder) + && Objects.equals(this.decoder, that.decoder) + && Objects.equals(this.contract, that.contract); } @Override public int hashCode() { - return Objects.hash(loggerLevel, connectTimeout, readTimeout, retryer, - errorDecoder, requestInterceptors, decode404, encoder, decoder, contract); + return Objects.hash(this.loggerLevel, this.connectTimeout, this.readTimeout, + this.retryer, this.errorDecoder, this.requestInterceptors, + this.decode404, this.encoder, this.decoder, this.contract); } + } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientSpecification.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientSpecification.java index 5e34f56d..ff824c6c 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientSpecification.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientSpecification.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,11 @@ package org.springframework.cloud.openfeign; -import org.springframework.cloud.context.named.NamedContextFactory; - import java.util.Arrays; import java.util.Objects; +import org.springframework.cloud.context.named.NamedContextFactory; + /** * @author Dave Syer * @author Gregor Zurowski @@ -31,15 +31,16 @@ class FeignClientSpecification implements NamedContextFactory.Specification { private Class[] configuration; - public FeignClientSpecification() {} + FeignClientSpecification() { + } - public FeignClientSpecification(String name, Class[] configuration) { + FeignClientSpecification(String name, Class[] configuration) { this.name = name; this.configuration = configuration; } public String getName() { - return name; + return this.name; } public void setName(String name) { @@ -47,7 +48,7 @@ class FeignClientSpecification implements NamedContextFactory.Specification { } public Class[] getConfiguration() { - return configuration; + return this.configuration; } public void setConfiguration(Class[] configuration) { @@ -56,24 +57,27 @@ class FeignClientSpecification implements NamedContextFactory.Specification { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } FeignClientSpecification that = (FeignClientSpecification) o; - return Objects.equals(name, that.name) && - Arrays.equals(configuration, that.configuration); + return Objects.equals(this.name, that.name) + && Arrays.equals(this.configuration, that.configuration); } @Override public int hashCode() { - return Objects.hash(name, configuration); + return Objects.hash(this.name, this.configuration); } @Override public String toString() { - return new StringBuilder("FeignClientSpecification{") - .append("name='").append(name).append("', ") - .append("configuration=").append(Arrays.toString(configuration)) - .append("}").toString(); + return new StringBuilder("FeignClientSpecification{").append("name='") + .append(this.name).append("', ").append("configuration=") + .append(Arrays.toString(this.configuration)).append("}").toString(); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java index 090bf3f4..ce0e91dc 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; @@ -20,6 +19,16 @@ package org.springframework.cloud.openfeign; import java.util.ArrayList; import java.util.List; +import com.netflix.hystrix.HystrixCommand; +import feign.Contract; +import feign.Feign; +import feign.Logger; +import feign.Retryer; +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.hystrix.HystrixFeign; +import feign.optionals.OptionalDecoder; + import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; @@ -37,17 +46,6 @@ import org.springframework.core.convert.ConversionService; import org.springframework.format.support.DefaultFormattingConversionService; import org.springframework.format.support.FormattingConversionService; -import com.netflix.hystrix.HystrixCommand; - -import feign.Contract; -import feign.Feign; -import feign.Logger; -import feign.Retryer; -import feign.codec.Decoder; -import feign.codec.Encoder; -import feign.hystrix.HystrixFeign; -import feign.optionals.OptionalDecoder; - /** * @author Dave Syer * @author Venil Noronha @@ -70,7 +68,8 @@ public class FeignClientsConfiguration { @Bean @ConditionalOnMissingBean public Decoder feignDecoder() { - return new OptionalDecoder(new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); + return new OptionalDecoder( + new ResponseEntityDecoder(new SpringDecoder(this.messageConverters))); } @Bean @@ -88,24 +87,12 @@ public class FeignClientsConfiguration { @Bean public FormattingConversionService feignConversionService() { FormattingConversionService conversionService = new DefaultFormattingConversionService(); - for (FeignFormatterRegistrar feignFormatterRegistrar : feignFormatterRegistrars) { + for (FeignFormatterRegistrar feignFormatterRegistrar : this.feignFormatterRegistrars) { feignFormatterRegistrar.registerFormatters(conversionService); } return conversionService; } - @Configuration - @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class }) - protected static class HystrixFeignConfiguration { - @Bean - @Scope("prototype") - @ConditionalOnMissingBean - @ConditionalOnProperty(name = "feign.hystrix.enabled") - public Feign.Builder feignHystrixBuilder() { - return HystrixFeign.builder(); - } - } - @Bean @ConditionalOnMissingBean public Retryer feignRetryer() { @@ -122,7 +109,21 @@ public class FeignClientsConfiguration { @Bean @ConditionalOnMissingBean(FeignLoggerFactory.class) public FeignLoggerFactory feignLoggerFactory() { - return new DefaultFeignLoggerFactory(logger); + return new DefaultFeignLoggerFactory(this.logger); + } + + @Configuration + @ConditionalOnClass({ HystrixCommand.class, HystrixFeign.class }) + protected static class HystrixFeignConfiguration { + + @Bean + @Scope("prototype") + @ConditionalOnMissingBean + @ConditionalOnProperty(name = "feign.hystrix.enabled") + public Feign.Builder feignHystrixBuilder() { + return HystrixFeign.builder(); + } + } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java index 02beddb8..72e08cd5 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignClientsRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,8 +58,8 @@ import org.springframework.util.StringUtils; * @author Venil Noronha * @author Gang Li */ -class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, - ResourceLoaderAware, EnvironmentAware { +class FeignClientsRegistrar + implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware { // patterned after Spring Integration IntegrationComponentScanRegistrar // and RibbonClientsConfigurationRegistgrar @@ -68,7 +68,68 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, private Environment environment; - public FeignClientsRegistrar() { + FeignClientsRegistrar() { + } + + static void validateFallback(final Class clazz) { + Assert.isTrue(!clazz.isInterface(), + "Fallback class must implement the interface annotated by @FeignClient"); + } + + static void validateFallbackFactory(final Class clazz) { + Assert.isTrue(!clazz.isInterface(), "Fallback factory must produce instances " + + "of fallback classes that implement the interface annotated by @FeignClient"); + } + + static String getName(String name) { + if (!StringUtils.hasText(name)) { + return ""; + } + + String host = null; + try { + String url; + if (!name.startsWith("http://") && !name.startsWith("https://")) { + url = "http://" + name; + } + else { + url = name; + } + host = new URI(url).getHost(); + + } + catch (URISyntaxException e) { + } + Assert.state(host != null, "Service id not legal hostname (" + name + ")"); + return name; + } + + static String getUrl(String url) { + if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) { + if (!url.contains("://")) { + url = "http://" + url; + } + try { + new URL(url); + } + catch (MalformedURLException e) { + throw new IllegalArgumentException(url + " is malformed", e); + } + } + return url; + } + + static String getPath(String path) { + if (StringUtils.hasText(path)) { + path = path.trim(); + if (!path.startsWith("/")) { + path = "/" + path; + } + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + } + return path; } @Override @@ -182,7 +243,8 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, String alias = contextId + "FeignClient"; AbstractBeanDefinition beanDefinition = definition.getBeanDefinition(); - boolean primary = (Boolean)attributes.get("primary"); // has a default, won't be null + boolean primary = (Boolean) attributes.get("primary"); // has a default, won't be + // null beanDefinition.setPrimary(primary); @@ -204,19 +266,6 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, validateFallbackFactory(annotation.getClass("fallbackFactory")); } - static void validateFallback(final Class clazz) { - Assert.isTrue( - !clazz.isInterface(), - "Fallback class must implement the interface annotated by @FeignClient" - ); - } - - static void validateFallbackFactory(final Class clazz) { - Assert.isTrue(!clazz.isInterface(), - "Fallback factory must produce instances of fallback classes that implement the interface annotated by @FeignClient" - ); - } - /* for testing */ String getName(Map attributes) { String name = (String) attributes.get("serviceId"); if (!StringUtils.hasText(name)) { @@ -239,28 +288,6 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, return getName(contextId); } - static String getName(String name) { - if (!StringUtils.hasText(name)) { - return ""; - } - - String host = null; - try { - String url; - if (!name.startsWith("http://") && !name.startsWith("https://")) { - url = "http://" + name; - } else { - url = name; - } - host = new URI(url).getHost(); - - } - catch (URISyntaxException e) { - } - Assert.state(host != null, "Service id not legal hostname (" + name + ")"); - return name; - } - private String resolve(String value) { if (StringUtils.hasText(value)) { return this.environment.resolvePlaceholders(value); @@ -273,43 +300,16 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, return getUrl(url); } - static String getUrl(String url) { - if (StringUtils.hasText(url) && !(url.startsWith("#{") && url.contains("}"))) { - if (!url.contains("://")) { - url = "http://" + url; - } - try { - new URL(url); - } - catch (MalformedURLException e) { - throw new IllegalArgumentException(url + " is malformed", e); - } - } - return url; - } - private String getPath(Map attributes) { String path = resolve((String) attributes.get("path")); return getPath(path); } - static String getPath(String path) { - if (StringUtils.hasText(path)) { - path = path.trim(); - if (!path.startsWith("/")) { - path = "/" + path; - } - if (path.endsWith("/")) { - path = path.substring(0, path.length() - 1); - } - } - return path; - } - protected ClassPathScanningCandidateComponentProvider getScanner() { return new ClassPathScanningCandidateComponentProvider(false, this.environment) { @Override - protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) { + protected boolean isCandidateComponent( + AnnotatedBeanDefinition beanDefinition) { boolean isCandidate = false; if (beanDefinition.getMetadata().isIndependent()) { if (!beanDefinition.getMetadata().isAnnotation()) { @@ -346,7 +346,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, } return basePackages; } - + private String getQualifier(Map client) { if (client == null) { return null; @@ -408,10 +408,9 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, /** * Creates a new {@link AllTypeFilter} to match if all the given delegates match. - * * @param delegates must not be {@literal null}. */ - public AllTypeFilter(List delegates) { + AllTypeFilter(List delegates) { Assert.notNull(delegates, "This argument is required, it must not be null"); this.delegates = delegates; } @@ -428,5 +427,7 @@ class FeignClientsRegistrar implements ImportBeanDefinitionRegistrar, return true; } + } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignContext.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignContext.java index fd484e77..a31fc473 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignContext.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignFormatterRegistrar.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignFormatterRegistrar.java index 8408b778..28d4bc36 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignFormatterRegistrar.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignFormatterRegistrar.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignLoggerFactory.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignLoggerFactory.java index 93588134..51aa8db1 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignLoggerFactory.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/FeignLoggerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,10 +27,9 @@ public interface FeignLoggerFactory { /** * Factory method to provide a {@link Logger} for a given {@link Class}. - * * @param type the {@link Class} for which a {@link Logger} instance is to be created * @return a {@link Logger} instance */ - public Logger create(Class type); + Logger create(Class type); } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/HystrixTargeter.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/HystrixTargeter.java index 6512f904..6a988880 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/HystrixTargeter.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/HystrixTargeter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; @@ -31,66 +30,70 @@ import feign.hystrix.SetterFactory; class HystrixTargeter implements Targeter { @Override - public T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, - Target.HardCodedTarget target) { + public T target(FeignClientFactoryBean factory, Feign.Builder feign, + FeignContext context, Target.HardCodedTarget target) { if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) { return feign.target(target); } feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign; SetterFactory setterFactory = getOptional(factory.getName(), context, - SetterFactory.class); + SetterFactory.class); if (setterFactory != null) { builder.setterFactory(setterFactory); } Class fallback = factory.getFallback(); if (fallback != void.class) { - return targetWithFallback(factory.getName(), context, target, builder, fallback); + return targetWithFallback(factory.getName(), context, target, builder, + fallback); } Class fallbackFactory = factory.getFallbackFactory(); if (fallbackFactory != void.class) { - return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory); + return targetWithFallbackFactory(factory.getName(), context, target, builder, + fallbackFactory); } return feign.target(target); } private T targetWithFallbackFactory(String feignClientName, FeignContext context, - Target.HardCodedTarget target, - HystrixFeign.Builder builder, - Class fallbackFactoryClass) { - FallbackFactory fallbackFactory = (FallbackFactory) - getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class); + Target.HardCodedTarget target, HystrixFeign.Builder builder, + Class fallbackFactoryClass) { + FallbackFactory fallbackFactory = (FallbackFactory) getFromContext( + "fallbackFactory", feignClientName, context, fallbackFactoryClass, + FallbackFactory.class); return builder.target(target, fallbackFactory); } - private T targetWithFallback(String feignClientName, FeignContext context, - Target.HardCodedTarget target, - HystrixFeign.Builder builder, Class fallback) { - T fallbackInstance = getFromContext("fallback", feignClientName, context, fallback, target.type()); + Target.HardCodedTarget target, HystrixFeign.Builder builder, + Class fallback) { + T fallbackInstance = getFromContext("fallback", feignClientName, context, + fallback, target.type()); return builder.target(target, fallbackInstance); } - private T getFromContext(String fallbackMechanism, String feignClientName, FeignContext context, - Class beanType, Class targetType) { + private T getFromContext(String fallbackMechanism, String feignClientName, + FeignContext context, Class beanType, Class targetType) { Object fallbackInstance = context.getInstance(feignClientName, beanType); if (fallbackInstance == null) { throw new IllegalStateException(String.format( - "No " + fallbackMechanism + " instance of type %s found for feign client %s", - beanType, feignClientName)); + "No " + fallbackMechanism + + " instance of type %s found for feign client %s", + beanType, feignClientName)); } if (!targetType.isAssignableFrom(beanType)) { - throw new IllegalStateException( - String.format( - "Incompatible " + fallbackMechanism + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s", - beanType, targetType, feignClientName)); + throw new IllegalStateException(String.format("Incompatible " + + fallbackMechanism + + " instance. Fallback/fallbackFactory of type %s is not assignable to %s for feign client %s", + beanType, targetType, feignClientName)); } return (T) fallbackInstance; } private T getOptional(String feignClientName, FeignContext context, - Class beanType) { + Class beanType) { return context.getInstance(feignClientName, beanType); } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/SpringQueryMap.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/SpringQueryMap.java index 6d919076..8090f3ef 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/SpringQueryMap.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/SpringQueryMap.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,14 +16,15 @@ package org.springframework.cloud.openfeign; -import feign.QueryMap; -import org.springframework.core.annotation.AliasFor; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import feign.QueryMap; + +import org.springframework.core.annotation.AliasFor; + /** * Spring MVC equivalent of OpenFeign's {@link feign.QueryMap} parameter annotation. * @@ -32,22 +33,21 @@ import java.lang.annotation.Target; * @see org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor */ @Retention(RetentionPolicy.RUNTIME) -@Target({ElementType.PARAMETER}) +@Target({ ElementType.PARAMETER }) public @interface SpringQueryMap { - /** - * Alias for {@link #encoded()}. - * - * @see QueryMap#encoded() - */ - @AliasFor("encoded") - boolean value() default false; + /** + * @see QueryMap#encoded() + * @return alias for {@link #encoded()}. + */ + @AliasFor("encoded") + boolean value() default false; + + /** + * @see QueryMap#encoded() + * @return Specifies whether parameter names and values are already encoded. + */ + @AliasFor("value") + boolean encoded() default false; - /** - * Specifies whether parameter names and values are already encoded. - * - * @see QueryMap#encoded() - */ - @AliasFor("value") - boolean encoded() default false; } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/Targeter.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/Targeter.java index fe74ebcc..abceb332 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/Targeter.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/Targeter.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; @@ -24,6 +23,8 @@ import feign.Target; * @author Spencer Gibb */ interface Targeter { - T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context, - Target.HardCodedTarget target); + + T target(FeignClientFactoryBean factory, Feign.Builder feign, + FeignContext context, Target.HardCodedTarget target); + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/PathVariableParameterProcessor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/PathVariableParameterProcessor.java index edf6e985..8e680285 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/PathVariableParameterProcessor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/PathVariableParameterProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +21,11 @@ import java.lang.reflect.Method; import java.util.Collection; import java.util.Map; +import feign.MethodMetadata; + import org.springframework.cloud.openfeign.AnnotatedParameterProcessor; import org.springframework.web.bind.annotation.PathVariable; -import feign.MethodMetadata; - import static feign.Util.checkState; import static feign.Util.emptyToNull; @@ -46,10 +46,12 @@ public class PathVariableParameterProcessor implements AnnotatedParameterProcess } @Override - public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) { + public boolean processArgument(AnnotatedParameterContext context, + Annotation annotation, Method method) { String name = ANNOTATION.cast(annotation).value(); checkState(emptyToNull(name) != null, - "PathVariable annotation was empty on param %s.", context.getParameterIndex()); + "PathVariable annotation was empty on param %s.", + context.getParameterIndex()); context.setParameterName(name); MethodMetadata data = context.getMethodMetadata(); @@ -74,4 +76,5 @@ public class PathVariableParameterProcessor implements AnnotatedParameterProcess } return false; } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/QueryMapParameterProcessor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/QueryMapParameterProcessor.java index e06bff80..ce1f0179 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/QueryMapParameterProcessor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/QueryMapParameterProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,14 @@ package org.springframework.cloud.openfeign.annotation; -import feign.MethodMetadata; -import org.springframework.cloud.openfeign.AnnotatedParameterProcessor; -import org.springframework.cloud.openfeign.SpringQueryMap; - import java.lang.annotation.Annotation; import java.lang.reflect.Method; +import feign.MethodMetadata; + +import org.springframework.cloud.openfeign.AnnotatedParameterProcessor; +import org.springframework.cloud.openfeign.SpringQueryMap; + /** * {@link SpringQueryMap} parameter processor. * @@ -39,7 +40,8 @@ public class QueryMapParameterProcessor implements AnnotatedParameterProcessor { } @Override - public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) { + public boolean processArgument(AnnotatedParameterContext context, + Annotation annotation, Method method) { int paramIndex = context.getParameterIndex(); MethodMetadata metadata = context.getMethodMetadata(); if (metadata.queryMapIndex() == null) { @@ -48,4 +50,5 @@ public class QueryMapParameterProcessor implements AnnotatedParameterProcessor { } return true; } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/RequestHeaderParameterProcessor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/RequestHeaderParameterProcessor.java index da8f0de9..62cdd8e7 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/RequestHeaderParameterProcessor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/RequestHeaderParameterProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,11 +21,11 @@ import java.lang.reflect.Method; import java.util.Collection; import java.util.Map; +import feign.MethodMetadata; + import org.springframework.cloud.openfeign.AnnotatedParameterProcessor; import org.springframework.web.bind.annotation.RequestHeader; -import feign.MethodMetadata; - import static feign.Util.checkState; import static feign.Util.emptyToNull; @@ -46,13 +46,15 @@ public class RequestHeaderParameterProcessor implements AnnotatedParameterProces } @Override - public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) { + public boolean processArgument(AnnotatedParameterContext context, + Annotation annotation, Method method) { int parameterIndex = context.getParameterIndex(); Class parameterType = method.getParameterTypes()[parameterIndex]; MethodMetadata data = context.getMethodMetadata(); if (Map.class.isAssignableFrom(parameterType)) { - checkState(data.headerMapIndex() == null, "Header map can only be present once."); + checkState(data.headerMapIndex() == null, + "Header map can only be present once."); data.headerMapIndex(parameterIndex); return true; @@ -63,8 +65,10 @@ public class RequestHeaderParameterProcessor implements AnnotatedParameterProces "RequestHeader.value() was empty on parameter %s", parameterIndex); context.setParameterName(name); - Collection header = context.setTemplateParameter(name, data.template().headers().get(name)); + Collection header = context.setTemplateParameter(name, + data.template().headers().get(name)); data.template().header(name, header); return true; } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/RequestParamParameterProcessor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/RequestParamParameterProcessor.java index 2fbe6807..ae5d416d 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/RequestParamParameterProcessor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/annotation/RequestParamParameterProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,14 +21,14 @@ import java.lang.reflect.Method; import java.util.Collection; import java.util.Map; +import feign.MethodMetadata; + import org.springframework.cloud.openfeign.AnnotatedParameterProcessor; import org.springframework.web.bind.annotation.RequestParam; import static feign.Util.checkState; import static feign.Util.emptyToNull; -import feign.MethodMetadata; - /** * {@link RequestParam} parameter processor. * @@ -46,13 +46,15 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess } @Override - public boolean processArgument(AnnotatedParameterContext context, Annotation annotation, Method method) { + public boolean processArgument(AnnotatedParameterContext context, + Annotation annotation, Method method) { int parameterIndex = context.getParameterIndex(); Class parameterType = method.getParameterTypes()[parameterIndex]; MethodMetadata data = context.getMethodMetadata(); if (Map.class.isAssignableFrom(parameterType)) { - checkState(data.queryMapIndex() == null, "Query map can only be present once."); + checkState(data.queryMapIndex() == null, + "Query map can only be present once."); data.queryMapIndex(parameterIndex); return true; @@ -61,8 +63,7 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess RequestParam requestParam = ANNOTATION.cast(annotation); String name = requestParam.value(); checkState(emptyToNull(name) != null, - "RequestParam.value() was empty on parameter %s", - parameterIndex); + "RequestParam.value() was empty on parameter %s", parameterIndex); context.setParameterName(name); Collection query = context.setTemplateParameter(name, @@ -70,4 +71,5 @@ public class RequestParamParameterProcessor implements AnnotatedParameterProcess data.template().query(name, query); return true; } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/BaseRequestInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/BaseRequestInterceptor.java index fefcc99b..0c24f852 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/BaseRequestInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/BaseRequestInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ package org.springframework.cloud.openfeign.encoding; import feign.RequestInterceptor; import feign.RequestTemplate; + import org.springframework.util.Assert; /** @@ -34,7 +35,6 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor { /** * Creates new instance of {@link BaseRequestInterceptor}. - * * @param properties the encoding properties */ protected BaseRequestInterceptor(FeignClientEncodingProperties properties) { @@ -44,12 +44,12 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor { /** * Adds the header if it wasn't yet specified. - * * @param requestTemplate the request - * @param name the header name - * @param values the header values + * @param name the header name + * @param values the header values */ - protected void addHeader(RequestTemplate requestTemplate, String name, String... values) { + protected void addHeader(RequestTemplate requestTemplate, String name, + String... values) { if (!requestTemplate.headers().containsKey(name)) { requestTemplate.header(name, values); @@ -57,7 +57,7 @@ public abstract class BaseRequestInterceptor implements RequestInterceptor { } protected FeignClientEncodingProperties getProperties() { - return properties; + return this.properties; } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingAutoConfiguration.java index da7731e9..b148cf55 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -40,14 +40,16 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnClass(Feign.class) @ConditionalOnBean(Client.class) @ConditionalOnProperty(value = "feign.compression.response.enabled", matchIfMissing = false) -//The OK HTTP client uses "transparent" compression. -//If the accept-encoding header is present it disable transparent compression +// The OK HTTP client uses "transparent" compression. +// If the accept-encoding header is present it disable transparent compression @ConditionalOnMissingBean(type = "okhttp3.OkHttpClient") @AutoConfigureAfter(FeignAutoConfiguration.class) public class FeignAcceptGzipEncodingAutoConfiguration { @Bean - public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) { + public FeignAcceptGzipEncodingInterceptor feignAcceptGzipEncodingInterceptor( + FeignClientEncodingProperties properties) { return new FeignAcceptGzipEncodingInterceptor(properties); } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingInterceptor.java index 9753e76a..ac2533e7 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignAcceptGzipEncodingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,11 @@ package org.springframework.cloud.openfeign.encoding; import feign.RequestTemplate; /** - * Enables the HTTP response payload compression by specifying the {@code Accept-Encoding} headers. - * Although this does not yet mean that the requests will be compressed, it requires the remote server - * to understand the header and be configured to compress responses. Still no all responses might be compressed - * based on the media type matching and other factors like the response content length. + * Enables the HTTP response payload compression by specifying the {@code Accept-Encoding} + * headers. Although this does not yet mean that the requests will be compressed, it + * requires the remote server to understand the header and be configured to compress + * responses. Still no all responses might be compressed based on the media type matching + * and other factors like the response content length. * * @author Jakub Narloch */ @@ -30,10 +31,10 @@ public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor { /** * Creates new instance of {@link FeignAcceptGzipEncodingInterceptor}. - * * @param properties the encoding properties */ - protected FeignAcceptGzipEncodingInterceptor(FeignClientEncodingProperties properties) { + protected FeignAcceptGzipEncodingInterceptor( + FeignClientEncodingProperties properties) { super(properties); } @@ -43,7 +44,8 @@ public class FeignAcceptGzipEncodingInterceptor extends BaseRequestInterceptor { @Override public void apply(RequestTemplate template) { - addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING, - HttpEncoding.DEFLATE_ENCODING); + addHeader(template, HttpEncoding.ACCEPT_ENCODING_HEADER, + HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING); } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignClientEncodingProperties.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignClientEncodingProperties.java index d8307b9f..51a9fcff 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignClientEncodingProperties.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignClientEncodingProperties.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,11 +16,11 @@ package org.springframework.cloud.openfeign.encoding; -import org.springframework.boot.context.properties.ConfigurationProperties; - import java.util.Arrays; import java.util.Objects; +import org.springframework.boot.context.properties.ConfigurationProperties; + /** * The Feign encoding properties. * @@ -32,7 +32,8 @@ public class FeignClientEncodingProperties { /** * The list of supported mime types. */ - private String[] mimeTypes = new String[]{"text/xml", "application/xml", "application/json"}; + private String[] mimeTypes = new String[] { "text/xml", "application/xml", + "application/json" }; /** * The minimum threshold content size. @@ -40,7 +41,7 @@ public class FeignClientEncodingProperties { private int minRequestSize = 2048; public String[] getMimeTypes() { - return mimeTypes; + return this.mimeTypes; } public void setMimeTypes(String[] mimeTypes) { @@ -48,7 +49,7 @@ public class FeignClientEncodingProperties { } public int getMinRequestSize() { - return minRequestSize; + return this.minRequestSize; } public void setMinRequestSize(int minRequestSize) { @@ -57,24 +58,28 @@ public class FeignClientEncodingProperties { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } FeignClientEncodingProperties that = (FeignClientEncodingProperties) o; - return Arrays.equals(mimeTypes, that.mimeTypes) && - Objects.equals(minRequestSize, that.minRequestSize); + return Arrays.equals(this.mimeTypes, that.mimeTypes) + && Objects.equals(this.minRequestSize, that.minRequestSize); } @Override public int hashCode() { - return Objects.hash(mimeTypes, minRequestSize); + return Objects.hash(this.mimeTypes, this.minRequestSize); } @Override public String toString() { - return new StringBuilder("FeignClientEncodingProperties{") - .append("mimeTypes=").append(Arrays.toString(mimeTypes)).append(", ") - .append("minRequestSize=").append(minRequestSize) - .append("}").toString(); + return new StringBuilder("FeignClientEncodingProperties{").append("mimeTypes=") + .append(Arrays.toString(this.mimeTypes)).append(", ") + .append("minRequestSize=").append(this.minRequestSize).append("}") + .toString(); } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingAutoConfiguration.java index 746377ca..bd6794cb 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,15 +39,17 @@ import org.springframework.context.annotation.Configuration; @EnableConfigurationProperties(FeignClientEncodingProperties.class) @ConditionalOnClass(Feign.class) @ConditionalOnBean(Client.class) -//The OK HTTP client uses "transparent" compression. -//If the content-encoding header is present it disable transparent compression +// The OK HTTP client uses "transparent" compression. +// If the content-encoding header is present it disable transparent compression @ConditionalOnMissingBean(type = "okhttp3.OkHttpClient") @ConditionalOnProperty(value = "feign.compression.request.enabled", matchIfMissing = false) @AutoConfigureAfter(FeignAutoConfiguration.class) public class FeignContentGzipEncodingAutoConfiguration { @Bean - public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) { + public FeignContentGzipEncodingInterceptor feignContentGzipEncodingInterceptor( + FeignClientEncodingProperties properties) { return new FeignContentGzipEncodingInterceptor(properties); } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java index bfe89d4e..77748987 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/FeignContentGzipEncodingInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,14 @@ package org.springframework.cloud.openfeign.encoding; -import feign.RequestTemplate; - import java.util.Collection; import java.util.Map; +import feign.RequestTemplate; + /** - * Enables the HTTP request payload compression by specifying the {@code Content-Encoding} headers. + * Enables the HTTP request payload compression by specifying the {@code Content-Encoding} + * headers. * * @author Jakub Narloch */ @@ -30,10 +31,10 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor /** * Creates new instance of {@link FeignContentGzipEncodingInterceptor}. - * * @param properties the encoding properties */ - protected FeignContentGzipEncodingInterceptor(FeignClientEncodingProperties properties) { + protected FeignContentGzipEncodingInterceptor( + FeignClientEncodingProperties properties) { super(properties); } @@ -44,14 +45,13 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor public void apply(RequestTemplate template) { if (requiresCompression(template)) { - addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER, HttpEncoding.GZIP_ENCODING, - HttpEncoding.DEFLATE_ENCODING); + addHeader(template, HttpEncoding.CONTENT_ENCODING_HEADER, + HttpEncoding.GZIP_ENCODING, HttpEncoding.DEFLATE_ENCODING); } } /** * Returns whether the request requires GZIP compression. - * * @param template the request template * @return true if request requires compression, false otherwise */ @@ -64,7 +64,6 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor /** * Returns whether the request content length exceed configured minimum size. - * * @param contentLength the content length header value * @return true if length is grater than minimum size, false otherwise */ @@ -78,14 +77,14 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor final String strLen = contentLength.iterator().next(); final long length = Long.parseLong(strLen); return length > getProperties().getMinRequestSize(); - } catch (NumberFormatException ex) { + } + catch (NumberFormatException ex) { return false; } } /** * Returns whether the content mime types matches the configures mime types. - * * @param contentTypes the content types * @return true if any specified content type matches the request content types */ @@ -94,7 +93,8 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor return false; } - if (getProperties().getMimeTypes() == null || getProperties().getMimeTypes().length == 0) { + if (getProperties().getMimeTypes() == null + || getProperties().getMimeTypes().length == 0) { // no specific mime types has been set - matching everything return true; } @@ -107,4 +107,5 @@ public class FeignContentGzipEncodingInterceptor extends BaseRequestInterceptor return false; } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/HttpEncoding.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/HttpEncoding.java index 58ec0a00..3bcde7f3 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/HttpEncoding.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/encoding/HttpEncoding.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,4 +52,5 @@ public interface HttpEncoding { * The Deflate encoding. */ String DEFLATE_ENCODING = "deflate"; + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/CachingSpringLoadBalancerFactory.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/CachingSpringLoadBalancerFactory.java index e47ea3ab..5846c99b 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/CachingSpringLoadBalancerFactory.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/CachingSpringLoadBalancerFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,14 +18,14 @@ package org.springframework.cloud.openfeign.ribbon; import java.util.Map; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; + import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory; import org.springframework.cloud.netflix.ribbon.ServerIntrospector; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.util.ConcurrentReferenceHashMap; -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.ILoadBalancer; - /** * Factory for SpringLoadBalancer instances that caches the entries created. * @@ -37,6 +37,7 @@ import com.netflix.loadbalancer.ILoadBalancer; public class CachingSpringLoadBalancerFactory { protected final SpringClientFactory factory; + protected LoadBalancedRetryFactory loadBalancedRetryFactory = null; private volatile Map cache = new ConcurrentReferenceHashMap<>(); @@ -45,23 +46,27 @@ public class CachingSpringLoadBalancerFactory { this.factory = factory; } - public CachingSpringLoadBalancerFactory(SpringClientFactory factory, LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) { + public CachingSpringLoadBalancerFactory(SpringClientFactory factory, + LoadBalancedRetryFactory loadBalancedRetryPolicyFactory) { this.factory = factory; this.loadBalancedRetryFactory = loadBalancedRetryPolicyFactory; } public FeignLoadBalancer create(String clientName) { FeignLoadBalancer client = this.cache.get(clientName); - if(client != null) { + if (client != null) { return client; } IClientConfig config = this.factory.getClientConfig(clientName); ILoadBalancer lb = this.factory.getLoadBalancer(clientName); - ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, ServerIntrospector.class); - client = loadBalancedRetryFactory != null ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector, - loadBalancedRetryFactory) : new FeignLoadBalancer(lb, config, serverIntrospector); + ServerIntrospector serverIntrospector = this.factory.getInstance(clientName, + ServerIntrospector.class); + client = this.loadBalancedRetryFactory != null + ? new RetryableFeignLoadBalancer(lb, config, serverIntrospector, + this.loadBalancedRetryFactory) + : new FeignLoadBalancer(lb, config, serverIntrospector); this.cache.put(clientName, client); return client; } -} \ No newline at end of file +} diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/DefaultFeignLoadBalancedConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/DefaultFeignLoadBalancedConfiguration.java index 68b52485..40b0a72f 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/DefaultFeignLoadBalancedConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/DefaultFeignLoadBalancedConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,12 +12,12 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.ribbon; import feign.Client; + import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.context.annotation.Bean; @@ -28,11 +28,13 @@ import org.springframework.context.annotation.Configuration; */ @Configuration class DefaultFeignLoadBalancedConfiguration { + @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, - SpringClientFactory clientFactory) { - return new LoadBalancerFeignClient(new Client.Default(null, null), - cachingFactory, clientFactory); + SpringClientFactory clientFactory) { + return new LoadBalancerFeignClient(new Client.Default(null, null), cachingFactory, + clientFactory); } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignLoadBalancer.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignLoadBalancer.java index cbffd228..0c10741e 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignLoadBalancer.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignLoadBalancer.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -57,13 +57,17 @@ public class FeignLoadBalancer extends AbstractLoadBalancerAwareClient { private final RibbonProperties ribbon; + protected int connectTimeout; + protected int readTimeout; + protected IClientConfig clientConfig; + protected ServerIntrospector serverIntrospector; public FeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, - ServerIntrospector serverIntrospector) { + ServerIntrospector serverIntrospector) { super(lb, clientConfig); this.setRetryHandler(RetryHandler.DEFAULT); this.clientConfig = clientConfig; @@ -80,8 +84,7 @@ public class FeignLoadBalancer extends Request.Options options; if (configOverride != null) { RibbonProperties override = RibbonProperties.from(configOverride); - options = new Request.Options( - override.connectTimeout(this.connectTimeout), + options = new Request.Options(override.connectTimeout(this.connectTimeout), override.readTimeout(this.readTimeout)); } else { @@ -110,13 +113,15 @@ public class FeignLoadBalancer extends @Override public URI reconstructURIWithServer(Server server, URI original) { - URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig, this.serverIntrospector, server); + URI uri = updateToSecureConnectionIfNeeded(original, this.clientConfig, + this.serverIntrospector, server); return super.reconstructURIWithServer(server, uri); } protected static class RibbonRequest extends ClientRequest implements Cloneable { private final Request request; + private final Client client; protected RibbonRequest(Client client, Request request, URI uri) { @@ -161,8 +166,9 @@ public class FeignLoadBalancer extends @Override public HttpHeaders getHeaders() { Map> headers = new HashMap<>(); - Map> feignHeaders = RibbonRequest.this.toRequest().headers(); - for(String key : feignHeaders.keySet()) { + Map> feignHeaders = RibbonRequest.this + .toRequest().headers(); + for (String key : feignHeaders.keySet()) { headers.put(key, new ArrayList(feignHeaders.get(key))); } HttpHeaders httpHeaders = new HttpHeaders(); @@ -174,22 +180,24 @@ public class FeignLoadBalancer extends } public Request getRequest() { - return request; + return this.request; } public Client getClient() { - return client; + return this.client; } @Override public Object clone() { return new RibbonRequest(this.client, this.request, getUri()); } + } protected static class RibbonResponse implements IResponse { private final URI uri; + private final Response response; protected RibbonResponse(URI uri, Response response) { @@ -235,4 +243,4 @@ public class FeignLoadBalancer extends } -} \ No newline at end of file +} diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignRetryPolicy.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignRetryPolicy.java index 56dfa7e9..d3dcf23f 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignRetryPolicy.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignRetryPolicy.java @@ -1,19 +1,17 @@ /* + * Copyright 2013-2019 the original author or authors. * - * * Copyright 2013-2016 the original author or authors. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.cloud.openfeign.ribbon; @@ -34,9 +32,13 @@ import org.springframework.retry.RetryContext; * @author Ryan Baxter */ public class FeignRetryPolicy extends InterceptorRetryPolicy { + private HttpRequest request; + private String serviceId; - public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, ServiceInstanceChooser serviceInstanceChooser, String serviceName) { + + public FeignRetryPolicy(HttpRequest request, LoadBalancedRetryPolicy policy, + ServiceInstanceChooser serviceInstanceChooser, String serviceName) { super(request, policy, serviceInstanceChooser, serviceName); this.request = request; this.serviceId = serviceName; @@ -45,17 +47,18 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy { @Override public boolean canRetry(RetryContext context) { /* - * In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server if one is not - * set in the retry context and then return true. RetryTemplat calls the canRetry method of - * the policy even on its first execution. So the fact that we didnt have a service instance set - * in the RetryContext signaled that it was the first execution and we should return true. + * In InterceptorRetryPolicy.canRetry we ask the LoadBalancer to choose a server + * if one is not set in the retry context and then return true. RetryTemplat calls + * the canRetry method of the policy even on its first execution. So the fact that + * we didnt have a service instance set in the RetryContext signaled that it was + * the first execution and we should return true. * - * In the Feign scenario, Feign as actually already queried the load balancer for a service instance - * and we set that service instance in the context when we call the open method of the policy. So in - * the Feign case we just return true if the retry count is 0 indicating we haven't yet made a failed - * request. + * In the Feign scenario, Feign as actually already queried the load balancer for + * a service instance and we set that service instance in the context when we call + * the open method of the policy. So in the Feign case we just return true if the + * retry count is 0 indicating we haven't yet made a failed request. */ - if(context.getRetryCount() == 0) { + if (context.getRetryCount() == 0) { return true; } return super.canRetry(context); @@ -64,20 +67,25 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy { @Override public RetryContext open(RetryContext parent) { /* - * With Feign (unlike Ribbon) the request already has the URI for the service instance - * we are going to make the request to, so extract that information and set the service - * instance in the context. In the Ribbon scenario the URI in the request object still has - * the service id so we choose and set the service instance later on. + * With Feign (unlike Ribbon) the request already has the URI for the service + * instance we are going to make the request to, so extract that information and + * set the service instance in the context. In the Ribbon scenario the URI in the + * request object still has the service id so we choose and set the service + * instance later on. */ - LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent, this.request); - context.setServiceInstance(new FeignRetryPolicyServiceInstance(serviceId, request)); + LoadBalancedRetryContext context = new LoadBalancedRetryContext(parent, + this.request); + context.setServiceInstance( + new FeignRetryPolicyServiceInstance(this.serviceId, this.request)); return context; } class FeignRetryPolicyServiceInstance implements ServiceInstance { private String serviceId; + private HttpRequest request; + private Map metadata; FeignRetryPolicyServiceInstance(String serviceId, HttpRequest request) { @@ -88,32 +96,34 @@ public class FeignRetryPolicy extends InterceptorRetryPolicy { @Override public String getServiceId() { - return serviceId; + return this.serviceId; } @Override public String getHost() { - return request.getURI().getHost(); + return this.request.getURI().getHost(); } @Override public int getPort() { - return request.getURI().getPort(); + return this.request.getURI().getPort(); } @Override public boolean isSecure() { - return "https".equals(request.getURI().getScheme()); + return "https".equals(this.request.getURI().getScheme()); } @Override public URI getUri() { - return request.getURI(); + return this.request.getURI(); } @Override public Map getMetadata() { - return metadata; + return this.metadata; } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientAutoConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientAutoConfiguration.java index 603193eb..c1a2586b 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,25 +16,24 @@ package org.springframework.cloud.openfeign.ribbon; +import com.netflix.loadbalancer.ILoadBalancer; +import feign.Feign; +import feign.Request; + import org.springframework.boot.autoconfigure.AutoConfigureBefore; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory; +import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.context.annotation.Primary; -import com.netflix.loadbalancer.ILoadBalancer; - -import feign.Feign; -import feign.Request; - /** * Autoconfiguration to be activated if Feign is in use and needs to be use Ribbon as a * load balancer. @@ -45,8 +44,9 @@ import feign.Request; @Configuration @AutoConfigureBefore(FeignAutoConfiguration.class) @EnableConfigurationProperties({ FeignHttpClientProperties.class }) -//Order is important here, last should be the default, first should be optional -// see https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653 +// Order is important here, last should be the default, first should be optional +// see +// https://github.com/spring-cloud/spring-cloud-netflix/issues/2086#issuecomment-316281653 @Import({ HttpClientFeignLoadBalancedConfiguration.class, OkHttpFeignLoadBalancedConfiguration.class, DefaultFeignLoadBalancedConfiguration.class }) @@ -66,8 +66,7 @@ public class FeignRibbonClientAutoConfiguration { @ConditionalOnMissingBean @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate") public CachingSpringLoadBalancerFactory retryabeCachingLBClientFactory( - SpringClientFactory factory, - LoadBalancedRetryFactory retryFactory) { + SpringClientFactory factory, LoadBalancedRetryFactory retryFactory) { return new CachingSpringLoadBalancerFactory(factory, retryFactory); } @@ -76,4 +75,5 @@ public class FeignRibbonClientAutoConfiguration { public Request.Options feignRequestOptions() { return LoadBalancerFeignClient.DEFAULT_OPTIONS; } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/HttpClientFeignLoadBalancedConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/HttpClientFeignLoadBalancedConfiguration.java index 90824608..73bc47b7 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/HttpClientFeignLoadBalancedConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/HttpClientFeignLoadBalancedConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,31 +12,32 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.ribbon; -import feign.Client; -import feign.httpclient.ApacheHttpClient; - import java.util.Timer; import java.util.TimerTask; + import javax.annotation.PreDestroy; + +import feign.Client; +import feign.httpclient.ApacheHttpClient; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.config.RegistryBuilder; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.commons.httpclient.ApacheHttpClientConnectionManagerFactory; import org.springframework.cloud.commons.httpclient.ApacheHttpClientFactory; -import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -48,9 +49,18 @@ import org.springframework.context.annotation.Configuration; @ConditionalOnProperty(value = "feign.httpclient.enabled", matchIfMissing = true) class HttpClientFeignLoadBalancedConfiguration { + @Bean + @ConditionalOnMissingBean(Client.class) + public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, + SpringClientFactory clientFactory, HttpClient httpClient) { + ApacheHttpClient delegate = new ApacheHttpClient(httpClient); + return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); + } + @Configuration @ConditionalOnMissingBean(CloseableHttpClient.class) protected static class HttpClientFeignConfiguration { + private final Timer connectionManagerTimer = new Timer( "FeignApacheHttpClientConfiguration.connectionManagerTimer", true); @@ -65,10 +75,12 @@ class HttpClientFeignLoadBalancedConfiguration { ApacheHttpClientConnectionManagerFactory connectionManagerFactory, FeignHttpClientProperties httpClientProperties) { final HttpClientConnectionManager connectionManager = connectionManagerFactory - .newConnectionManager(httpClientProperties.isDisableSslValidation(), httpClientProperties.getMaxConnections(), + .newConnectionManager(httpClientProperties.isDisableSslValidation(), + httpClientProperties.getMaxConnections(), httpClientProperties.getMaxConnectionsPerRoute(), httpClientProperties.getTimeToLive(), - httpClientProperties.getTimeToLiveUnit(), registryBuilder); + httpClientProperties.getTimeToLiveUnit(), + this.registryBuilder); this.connectionManagerTimer.schedule(new TimerTask() { @Override public void run() { @@ -80,49 +92,47 @@ class HttpClientFeignLoadBalancedConfiguration { @Bean @ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "true") - public CloseableHttpClient customHttpClient(HttpClientConnectionManager httpClientConnectionManager, - FeignHttpClientProperties httpClientProperties) { - HttpClientBuilder builder = HttpClientBuilder.create().disableCookieManagement().useSystemProperties(); - this.httpClient = createClient(builder, httpClientConnectionManager, httpClientProperties); + public CloseableHttpClient customHttpClient( + HttpClientConnectionManager httpClientConnectionManager, + FeignHttpClientProperties httpClientProperties) { + HttpClientBuilder builder = HttpClientBuilder.create() + .disableCookieManagement().useSystemProperties(); + this.httpClient = createClient(builder, httpClientConnectionManager, + httpClientProperties); return this.httpClient; } @Bean @ConditionalOnProperty(value = "feign.compression.response.enabled", havingValue = "false", matchIfMissing = true) - public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, HttpClientConnectionManager httpClientConnectionManager, - FeignHttpClientProperties httpClientProperties) { - this.httpClient = createClient(httpClientFactory.createBuilder(), httpClientConnectionManager, httpClientProperties); + public CloseableHttpClient httpClient(ApacheHttpClientFactory httpClientFactory, + HttpClientConnectionManager httpClientConnectionManager, + FeignHttpClientProperties httpClientProperties) { + this.httpClient = createClient(httpClientFactory.createBuilder(), + httpClientConnectionManager, httpClientProperties); return this.httpClient; } - private CloseableHttpClient createClient(HttpClientBuilder builder, HttpClientConnectionManager httpClientConnectionManager, - FeignHttpClientProperties httpClientProperties) { + private CloseableHttpClient createClient(HttpClientBuilder builder, + HttpClientConnectionManager httpClientConnectionManager, + FeignHttpClientProperties httpClientProperties) { RequestConfig defaultRequestConfig = RequestConfig.custom() .setConnectTimeout(httpClientProperties.getConnectionTimeout()) .setRedirectsEnabled(httpClientProperties.isFollowRedirects()) .build(); - CloseableHttpClient httpClient = builder.setDefaultRequestConfig(defaultRequestConfig). - setConnectionManager(httpClientConnectionManager).build(); + CloseableHttpClient httpClient = builder + .setDefaultRequestConfig(defaultRequestConfig) + .setConnectionManager(httpClientConnectionManager).build(); return httpClient; } @PreDestroy public void destroy() throws Exception { - connectionManagerTimer.cancel(); - if(httpClient != null) { - httpClient.close(); + this.connectionManagerTimer.cancel(); + if (this.httpClient != null) { + this.httpClient.close(); } } + } - - @Bean - @ConditionalOnMissingBean(Client.class) - public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, - SpringClientFactory clientFactory, HttpClient httpClient) { - ApacheHttpClient delegate = new ApacheHttpClient(httpClient); - return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); - } - - } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClient.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClient.java index 700d50c6..16476731 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClient.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2015-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,13 +19,10 @@ package org.springframework.cloud.openfeign.ribbon; import java.io.IOException; import java.net.URI; -import org.springframework.cloud.netflix.ribbon.SpringClientFactory; - import com.netflix.client.ClientException; import com.netflix.client.config.CommonClientConfigKey; import com.netflix.client.config.DefaultClientConfigImpl; import com.netflix.client.config.IClientConfig; - import feign.Client; import feign.Request; import feign.Response; @@ -41,17 +38,37 @@ public class LoadBalancerFeignClient implements Client { static final Request.Options DEFAULT_OPTIONS = new Request.Options(); private final Client delegate; + private CachingSpringLoadBalancerFactory lbClientFactory; + private SpringClientFactory clientFactory; public LoadBalancerFeignClient(Client delegate, - CachingSpringLoadBalancerFactory lbClientFactory, - SpringClientFactory clientFactory) { + CachingSpringLoadBalancerFactory lbClientFactory, + SpringClientFactory clientFactory) { this.delegate = delegate; this.lbClientFactory = lbClientFactory; this.clientFactory = clientFactory; } + static URI cleanUrl(String originalUrl, String host) { + String newUrl = originalUrl; + if (originalUrl.startsWith("https://")) { + newUrl = originalUrl.substring(0, 8) + + originalUrl.substring(8 + host.length()); + } + else if (originalUrl.startsWith("http")) { + newUrl = originalUrl.substring(0, 7) + + originalUrl.substring(7 + host.length()); + } + StringBuffer buffer = new StringBuffer(newUrl); + if ((newUrl.startsWith("https://") && newUrl.length() == 8) + || (newUrl.startsWith("http://") && newUrl.length() == 7)) { + buffer.append("/"); + } + return URI.create(buffer.toString()); + } + @Override public Response execute(Request request, Request.Options options) throws IOException { try { @@ -62,8 +79,8 @@ public class LoadBalancerFeignClient implements Client { this.delegate, request, uriWithoutHost); IClientConfig requestConfig = getClientConfig(options, clientName); - return lbClient(clientName).executeWithLoadBalancer(ribbonRequest, - requestConfig).toResponse(); + return lbClient(clientName) + .executeWithLoadBalancer(ribbonRequest, requestConfig).toResponse(); } catch (ClientException e) { IOException io = findIOException(e); @@ -78,7 +95,8 @@ public class LoadBalancerFeignClient implements Client { IClientConfig requestConfig; if (options == DEFAULT_OPTIONS) { requestConfig = this.clientFactory.getClientConfig(clientName); - } else { + } + else { requestConfig = new FeignOptionsClientConfig(options); } return requestConfig; @@ -98,28 +116,13 @@ public class LoadBalancerFeignClient implements Client { return this.delegate; } - static URI cleanUrl(String originalUrl, String host) { - String newUrl = originalUrl; - if(originalUrl.startsWith("https://")) { - newUrl = originalUrl.substring(0, 8) + originalUrl.substring(8 + host.length()); - } else if(originalUrl.startsWith("http")) { - newUrl = originalUrl.substring(0, 7) + originalUrl.substring(7 + host.length()); - } - StringBuffer buffer = new StringBuffer(newUrl); - if((newUrl.startsWith("https://") && newUrl.length() == 8) || - (newUrl.startsWith("http://") && newUrl.length() == 7)) { - buffer.append("/"); - } - return URI.create(buffer.toString()); - } - private FeignLoadBalancer lbClient(String clientName) { return this.lbClientFactory.create(clientName); } static class FeignOptionsClientConfig extends DefaultClientConfigImpl { - public FeignOptionsClientConfig(Request.Options options) { + FeignOptionsClientConfig(Request.Options options) { setProperty(CommonClientConfigKey.ConnectTimeout, options.connectTimeoutMillis()); setProperty(CommonClientConfigKey.ReadTimeout, options.readTimeoutMillis()); @@ -136,4 +139,5 @@ public class LoadBalancerFeignClient implements Client { } } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/OkHttpFeignLoadBalancedConfiguration.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/OkHttpFeignLoadBalancedConfiguration.java index 0e42d864..018c0ea2 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/OkHttpFeignLoadBalancedConfiguration.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/OkHttpFeignLoadBalancedConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,24 +12,25 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.ribbon; +import java.util.concurrent.TimeUnit; + +import javax.annotation.PreDestroy; + import feign.Client; import feign.okhttp.OkHttpClient; import okhttp3.ConnectionPool; -import java.util.concurrent.TimeUnit; -import javax.annotation.PreDestroy; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.cloud.commons.httpclient.OkHttpClientConnectionPoolFactory; import org.springframework.cloud.commons.httpclient.OkHttpClientFactory; -import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; +import org.springframework.cloud.openfeign.support.FeignHttpClientProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -38,18 +39,28 @@ import org.springframework.context.annotation.Configuration; */ @Configuration @ConditionalOnClass(OkHttpClient.class) -@ConditionalOnProperty(value = "feign.okhttp.enabled") +@ConditionalOnProperty("feign.okhttp.enabled") class OkHttpFeignLoadBalancedConfiguration { + @Bean + @ConditionalOnMissingBean(Client.class) + public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, + SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) { + OkHttpClient delegate = new OkHttpClient(okHttpClient); + return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); + } + @Configuration @ConditionalOnMissingBean(okhttp3.OkHttpClient.class) protected static class OkHttpFeignConfiguration { + private okhttp3.OkHttpClient okHttpClient; @Bean @ConditionalOnMissingBean(ConnectionPool.class) - public ConnectionPool httpClientConnectionPool(FeignHttpClientProperties httpClientProperties, - OkHttpClientConnectionPoolFactory connectionPoolFactory) { + public ConnectionPool httpClientConnectionPool( + FeignHttpClientProperties httpClientProperties, + OkHttpClientConnectionPoolFactory connectionPoolFactory) { Integer maxTotalConnections = httpClientProperties.getMaxConnections(); Long timeToLive = httpClientProperties.getTimeToLive(); TimeUnit ttlUnit = httpClientProperties.getTimeToLiveUnit(); @@ -58,30 +69,26 @@ class OkHttpFeignLoadBalancedConfiguration { @Bean public okhttp3.OkHttpClient client(OkHttpClientFactory httpClientFactory, - ConnectionPool connectionPool, FeignHttpClientProperties httpClientProperties) { + ConnectionPool connectionPool, + FeignHttpClientProperties httpClientProperties) { Boolean followRedirects = httpClientProperties.isFollowRedirects(); Integer connectTimeout = httpClientProperties.getConnectionTimeout(); - this.okHttpClient = httpClientFactory.createBuilder(httpClientProperties.isDisableSslValidation()). - connectTimeout(connectTimeout, TimeUnit.MILLISECONDS). - followRedirects(followRedirects). - connectionPool(connectionPool).build(); + this.okHttpClient = httpClientFactory + .createBuilder(httpClientProperties.isDisableSslValidation()) + .connectTimeout(connectTimeout, TimeUnit.MILLISECONDS) + .followRedirects(followRedirects).connectionPool(connectionPool) + .build(); return this.okHttpClient; } @PreDestroy public void destroy() { - if(okHttpClient != null) { - okHttpClient.dispatcher().executorService().shutdown(); - okHttpClient.connectionPool().evictAll(); + if (this.okHttpClient != null) { + this.okHttpClient.dispatcher().executorService().shutdown(); + this.okHttpClient.connectionPool().evictAll(); } } + } - @Bean - @ConditionalOnMissingBean(Client.class) - public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, - SpringClientFactory clientFactory, okhttp3.OkHttpClient okHttpClient) { - OkHttpClient delegate = new OkHttpClient(okHttpClient); - return new LoadBalancerFeignClient(delegate, cachingFactory, clientFactory); - } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancer.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancer.java index 82c629b4..82b7deaf 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancer.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancer.java @@ -1,28 +1,32 @@ /* + * Copyright 2013-2019 the original author or authors. * - * * Copyright 2013-2016 the original author or authors. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.cloud.openfeign.ribbon; +import java.io.IOException; +import java.net.URI; + +import com.netflix.client.DefaultLoadBalancerRetryHandler; +import com.netflix.client.RequestSpecificRetryHandler; +import com.netflix.client.config.IClientConfig; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; import feign.Request; import feign.Response; -import java.io.IOException; -import java.net.URI; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancedRecoveryCallback; import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryContext; @@ -40,71 +44,82 @@ import org.springframework.retry.backoff.NoBackOffPolicy; import org.springframework.retry.policy.NeverRetryPolicy; import org.springframework.retry.support.RetryTemplate; import org.springframework.util.StreamUtils; -import com.netflix.client.DefaultLoadBalancerRetryHandler; -import com.netflix.client.RequestSpecificRetryHandler; -import com.netflix.client.config.IClientConfig; -import com.netflix.loadbalancer.ILoadBalancer; -import com.netflix.loadbalancer.Server; /** * A {@link FeignLoadBalancer} that leverages Spring Retry to retry failed requests. + * * @author Ryan Baxter * @author Gang Li */ -public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements ServiceInstanceChooser { +public class RetryableFeignLoadBalancer extends FeignLoadBalancer + implements ServiceInstanceChooser { private final LoadBalancedRetryFactory loadBalancedRetryFactory; - public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector, - LoadBalancedRetryFactory loadBalancedRetryFactory) { + public RetryableFeignLoadBalancer(ILoadBalancer lb, IClientConfig clientConfig, + ServerIntrospector serverIntrospector, + LoadBalancedRetryFactory loadBalancedRetryFactory) { super(lb, clientConfig, serverIntrospector); this.loadBalancedRetryFactory = loadBalancedRetryFactory; this.setRetryHandler(new DefaultLoadBalancerRetryHandler(clientConfig)); } @Override - public RibbonResponse execute(final RibbonRequest request, IClientConfig configOverride) - throws IOException { + public RibbonResponse execute(final RibbonRequest request, + IClientConfig configOverride) throws IOException { final Request.Options options; if (configOverride != null) { RibbonProperties ribbon = RibbonProperties.from(configOverride); - options = new Request.Options( - ribbon.connectTimeout(this.connectTimeout), + options = new Request.Options(ribbon.connectTimeout(this.connectTimeout), ribbon.readTimeout(this.readTimeout)); } else { options = new Request.Options(this.connectTimeout, this.readTimeout); } - final LoadBalancedRetryPolicy retryPolicy = loadBalancedRetryFactory.createRetryPolicy(this.getClientName(), this); + final LoadBalancedRetryPolicy retryPolicy = this.loadBalancedRetryFactory + .createRetryPolicy(this.getClientName(), this); RetryTemplate retryTemplate = new RetryTemplate(); - BackOffPolicy backOffPolicy = loadBalancedRetryFactory.createBackOffPolicy(this.getClientName()); - retryTemplate.setBackOffPolicy(backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy); - RetryListener[] retryListeners = this.loadBalancedRetryFactory.createRetryListeners(this.getClientName()); + BackOffPolicy backOffPolicy = this.loadBalancedRetryFactory + .createBackOffPolicy(this.getClientName()); + retryTemplate.setBackOffPolicy( + backOffPolicy == null ? new NoBackOffPolicy() : backOffPolicy); + RetryListener[] retryListeners = this.loadBalancedRetryFactory + .createRetryListeners(this.getClientName()); if (retryListeners != null && retryListeners.length != 0) { retryTemplate.setListeners(retryListeners); } retryTemplate.setRetryPolicy(retryPolicy == null ? new NeverRetryPolicy() - : new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this, this.getClientName())); + : new FeignRetryPolicy(request.toHttpRequest(), retryPolicy, this, + this.getClientName())); return retryTemplate.execute(new RetryCallback() { @Override - public RibbonResponse doWithRetry(RetryContext retryContext) throws IOException { + public RibbonResponse doWithRetry(RetryContext retryContext) + throws IOException { Request feignRequest = null; - //on retries the policy will choose the server and set it in the context - //extract the server and update the request being made + // on retries the policy will choose the server and set it in the context + // extract the server and update the request being made if (retryContext instanceof LoadBalancedRetryContext) { - ServiceInstance service = ((LoadBalancedRetryContext) retryContext).getServiceInstance(); + ServiceInstance service = ((LoadBalancedRetryContext) retryContext) + .getServiceInstance(); if (service != null) { - feignRequest = ((RibbonRequest) request.replaceUri(reconstructURIWithServer(new Server(service.getHost(), service.getPort()), request.getUri()))).toRequest(); + feignRequest = ((RibbonRequest) request + .replaceUri(reconstructURIWithServer( + new Server(service.getHost(), service.getPort()), + request.getUri()))).toRequest(); } } if (feignRequest == null) { feignRequest = request.toRequest(); } Response response = request.client().execute(feignRequest, options); - if (retryPolicy != null && retryPolicy.retryableStatusCode(response.status())) { - byte[] byteArray = response.body() == null ? new byte[]{} : StreamUtils.copyToByteArray(response.body().asInputStream()); + if (retryPolicy != null + && retryPolicy.retryableStatusCode(response.status())) { + byte[] byteArray = response.body() == null ? new byte[] {} + : StreamUtils + .copyToByteArray(response.body().asInputStream()); response.close(); - throw new RibbonResponseStatusCodeException(RetryableFeignLoadBalancer.this.clientName, response, + throw new RibbonResponseStatusCodeException( + RetryableFeignLoadBalancer.this.clientName, response, byteArray, request.getUri()); } return new RibbonResponse(request.getUri(), response); @@ -120,7 +135,8 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser @Override public RequestSpecificRetryHandler getRequestSpecificRetryHandler( FeignLoadBalancer.RibbonRequest request, IClientConfig requestConfig) { - return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), requestConfig); + return new RequestSpecificRetryHandler(false, false, this.getRetryHandler(), + requestConfig); } @Override @@ -128,4 +144,5 @@ public class RetryableFeignLoadBalancer extends FeignLoadBalancer implements Ser return new RibbonLoadBalancerClient.RibbonServer(serviceId, this.getLoadBalancer().chooseServer(serviceId)); } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/RibbonResponseStatusCodeException.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/RibbonResponseStatusCodeException.java index d0025861..2840ad10 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/RibbonResponseStatusCodeException.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/ribbon/RibbonResponseStatusCodeException.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.cloud.openfeign.ribbon; import java.io.ByteArrayInputStream; @@ -23,22 +24,26 @@ import feign.Response; import org.springframework.cloud.client.loadbalancer.RetryableStatusCodeException; /** - * A {@link RetryableStatusCodeException} for {@link Response}s + * A {@link RetryableStatusCodeException} for {@link Response}s. + * * @author Ryan Baxter */ public class RibbonResponseStatusCodeException extends RetryableStatusCodeException { - private Response response; - public RibbonResponseStatusCodeException(String serviceId, Response response, byte[] body, URI uri) { + private final Response response; + + public RibbonResponseStatusCodeException(String serviceId, Response response, + byte[] body, URI uri) { super(serviceId, response.status(), response, uri); - this.response = Response.builder().body(new ByteArrayInputStream(body), body.length) + this.response = Response.builder() + .body(new ByteArrayInputStream(body), body.length) .headers(response.headers()).reason(response.reason()) .status(response.status()).request(response.request()).build(); } @Override public Response getResponse() { - return response; + return this.response; } } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FallbackCommand.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FallbackCommand.java index 244cc740..ff5b0f35 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FallbackCommand.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FallbackCommand.java @@ -1,3 +1,19 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.openfeign.support; import com.netflix.hystrix.HystrixCommand; @@ -6,8 +22,11 @@ import com.netflix.hystrix.HystrixThreadPoolKey; /** * Convenience class for implementing feign fallbacks that return {@link HystrixCommand}. - * Also useful for return types of {@link rx.Observable} and {@link java.util.concurrent.Future}. - * For those return types, just call {@link FallbackCommand#observe()} or {@link FallbackCommand#queue()} respectively. + * Also useful for return types of {@link rx.Observable} and + * {@link java.util.concurrent.Future}. For those return types, just call + * {@link FallbackCommand#observe()} or {@link FallbackCommand#queue()} respectively. + * + * @param result type * @author Spencer Gibb */ public class FallbackCommand extends HystrixCommand { @@ -28,17 +47,21 @@ public class FallbackCommand extends HystrixCommand { this.result = result; } - public FallbackCommand(T result, HystrixCommandGroupKey group, int executionIsolationThreadTimeoutInMilliseconds) { + public FallbackCommand(T result, HystrixCommandGroupKey group, + int executionIsolationThreadTimeoutInMilliseconds) { super(group, executionIsolationThreadTimeoutInMilliseconds); this.result = result; } - public FallbackCommand(T result, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool) { + public FallbackCommand(T result, HystrixCommandGroupKey group, + HystrixThreadPoolKey threadPool) { super(group, threadPool); this.result = result; } - public FallbackCommand(T result, HystrixCommandGroupKey group, HystrixThreadPoolKey threadPool, int executionIsolationThreadTimeoutInMilliseconds) { + public FallbackCommand(T result, HystrixCommandGroupKey group, + HystrixThreadPoolKey threadPool, + int executionIsolationThreadTimeoutInMilliseconds) { super(group, threadPool, executionIsolationThreadTimeoutInMilliseconds); this.result = result; } @@ -52,4 +75,5 @@ public class FallbackCommand extends HystrixCommand { protected T run() throws Exception { return this.result; } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java index 0449450a..402978c9 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignHttpClientProperties.java @@ -1,24 +1,23 @@ /* + * Copyright 2013-2019 the original author or authors. * - * * Copyright 2013-2016 the original author or authors. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.cloud.openfeign.support; import java.util.concurrent.TimeUnit; + import org.springframework.boot.context.properties.ConfigurationProperties; /** @@ -26,26 +25,65 @@ import org.springframework.boot.context.properties.ConfigurationProperties; */ @ConfigurationProperties(prefix = "feign.httpclient") public class FeignHttpClientProperties { + + /** + * Default value for disabling SSL validation. + */ public static final boolean DEFAULT_DISABLE_SSL_VALIDATION = false; + + /** + * Default value for max number od connections. + */ public static final int DEFAULT_MAX_CONNECTIONS = 200; + + /** + * Default value for max number od connections per route. + */ public static final int DEFAULT_MAX_CONNECTIONS_PER_ROUTE = 50; + + /** + * Default value for time to live. + */ public static final long DEFAULT_TIME_TO_LIVE = 900L; + + /** + * Default time to live unit. + */ public static final TimeUnit DEFAULT_TIME_TO_LIVE_UNIT = TimeUnit.SECONDS; + + /** + * Default value for following redirects. + */ public static final boolean DEFAULT_FOLLOW_REDIRECTS = true; + + /** + * Default value for connection timeout. + */ public static final int DEFAULT_CONNECTION_TIMEOUT = 2000; + + /** + * Default value for connection timer repeat. + */ public static final int DEFAULT_CONNECTION_TIMER_REPEAT = 3000; private boolean disableSslValidation = DEFAULT_DISABLE_SSL_VALIDATION; + private int maxConnections = DEFAULT_MAX_CONNECTIONS; + private int maxConnectionsPerRoute = DEFAULT_MAX_CONNECTIONS_PER_ROUTE; + private long timeToLive = DEFAULT_TIME_TO_LIVE; + private TimeUnit timeToLiveUnit = DEFAULT_TIME_TO_LIVE_UNIT; + private boolean followRedirects = DEFAULT_FOLLOW_REDIRECTS; + private int connectionTimeout = DEFAULT_CONNECTION_TIMEOUT; + private int connectionTimerRepeat = DEFAULT_CONNECTION_TIMER_REPEAT; public int getConnectionTimerRepeat() { - return connectionTimerRepeat; + return this.connectionTimerRepeat; } public void setConnectionTimerRepeat(int connectionTimerRepeat) { @@ -53,7 +91,7 @@ public class FeignHttpClientProperties { } public boolean isDisableSslValidation() { - return disableSslValidation; + return this.disableSslValidation; } public void setDisableSslValidation(boolean disableSslValidation) { @@ -61,7 +99,7 @@ public class FeignHttpClientProperties { } public int getMaxConnections() { - return maxConnections; + return this.maxConnections; } public void setMaxConnections(int maxConnections) { @@ -69,7 +107,7 @@ public class FeignHttpClientProperties { } public int getMaxConnectionsPerRoute() { - return maxConnectionsPerRoute; + return this.maxConnectionsPerRoute; } public void setMaxConnectionsPerRoute(int maxConnectionsPerRoute) { @@ -77,7 +115,7 @@ public class FeignHttpClientProperties { } public long getTimeToLive() { - return timeToLive; + return this.timeToLive; } public void setTimeToLive(long timeToLive) { @@ -85,7 +123,7 @@ public class FeignHttpClientProperties { } public TimeUnit getTimeToLiveUnit() { - return timeToLiveUnit; + return this.timeToLiveUnit; } public void setTimeToLiveUnit(TimeUnit timeToLiveUnit) { @@ -93,7 +131,7 @@ public class FeignHttpClientProperties { } public boolean isFollowRedirects() { - return followRedirects; + return this.followRedirects; } public void setFollowRedirects(boolean followRedirects) { @@ -101,10 +139,11 @@ public class FeignHttpClientProperties { } public int getConnectionTimeout() { - return connectionTimeout; + return this.connectionTimeout; } public void setConnectionTimeout(int connectionTimeout) { this.connectionTimeout = connectionTimeout; } + } diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignUtils.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignUtils.java index 20a214e9..12f208a5 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignUtils.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/FeignUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,11 @@ import static java.util.Optional.ofNullable; * @author Spencer Gibb * @author Olga Maciaszek-Sharma */ -public class FeignUtils { +public final class FeignUtils { + + private FeignUtils() { + throw new IllegalStateException("Can't instantiate a utility class"); + } static HttpHeaders getHttpHeaders(Map> headers) { HttpHeaders httpHeaders = new HttpHeaders(); diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/ResponseEntityDecoder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/ResponseEntityDecoder.java index 8f421329..4cd7a13a 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/ResponseEntityDecoder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/ResponseEntityDecoder.java @@ -1,3 +1,19 @@ +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package org.springframework.cloud.openfeign.support; import java.io.IOException; @@ -5,20 +21,21 @@ import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.LinkedList; +import feign.FeignException; +import feign.Response; +import feign.codec.Decoder; + import org.springframework.http.HttpEntity; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; -import feign.FeignException; -import feign.Response; -import feign.codec.Decoder; - /** * Decoder adds compatibility for Spring MVC's ResponseEntity to any other decoder via * composition. - * @author chadjaros + * + * @author chad jaros */ public class ResponseEntityDecoder implements Decoder { @@ -29,12 +46,12 @@ public class ResponseEntityDecoder implements Decoder { } @Override - public Object decode(final Response response, Type type) throws IOException, - FeignException { + public Object decode(final Response response, Type type) + throws IOException, FeignException { if (isParameterizeHttpEntity(type)) { type = ((ParameterizedType) type).getActualTypeArguments()[0]; - Object decodedObject = decoder.decode(response, type); + Object decodedObject = this.decoder.decode(response, type); return createResponse(decodedObject, response); } @@ -42,7 +59,7 @@ public class ResponseEntityDecoder implements Decoder { return createResponse(null, response); } else { - return decoder.decode(response, type); + return this.decoder.decode(response, type); } } @@ -69,7 +86,8 @@ public class ResponseEntityDecoder implements Decoder { headers.put(key, new LinkedList<>(response.headers().get(key))); } - return new ResponseEntity<>((T) instance, headers, HttpStatus.valueOf(response - .status())); + return new ResponseEntity<>((T) instance, headers, + HttpStatus.valueOf(response.status())); } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringDecoder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringDecoder.java index c5b85b50..3711d223 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringDecoder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringDecoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,19 +12,21 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.support; -import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders; - import java.io.IOException; import java.io.InputStream; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.WildcardType; +import feign.FeignException; +import feign.Response; +import feign.codec.DecodeException; +import feign.codec.Decoder; + import org.springframework.beans.factory.ObjectFactory; import org.springframework.boot.autoconfigure.http.HttpMessageConverters; import org.springframework.http.HttpHeaders; @@ -32,10 +34,7 @@ import org.springframework.http.HttpStatus; import org.springframework.http.client.ClientHttpResponse; import org.springframework.web.client.HttpMessageConverterExtractor; -import feign.FeignException; -import feign.Response; -import feign.codec.DecodeException; -import feign.codec.Decoder; +import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHeaders; /** * @author Spencer Gibb @@ -63,7 +62,7 @@ public class SpringDecoder implements Decoder { "type is not an instance of Class or ParameterizedType: " + type); } - private class FeignResponseAdapter implements ClientHttpResponse { + private final class FeignResponseAdapter implements ClientHttpResponse { private final Response response; diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java index 26f9ae0e..6725a459 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringEncoder.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.support; @@ -49,7 +48,7 @@ import static org.springframework.cloud.openfeign.support.FeignUtils.getHttpHead /** * @author Spencer Gibb - * @author ScienJus + * @author Scien Jus */ public class SpringEncoder implements Encoder { @@ -79,12 +78,13 @@ public class SpringEncoder implements Encoder { if (bodyType != null && bodyType.equals(MultipartFile.class)) { if (Objects.equals(requestContentType, MediaType.MULTIPART_FORM_DATA)) { - springFormEncoder.encode(requestBody, bodyType, request); + this.springFormEncoder.encode(requestBody, bodyType, request); return; - } else { - String message = "Content-Type \"" + MediaType.MULTIPART_FORM_DATA + - "\" not set for request body of type " + - requestBody.getClass().getSimpleName(); + } + else { + String message = "Content-Type \"" + MediaType.MULTIPART_FORM_DATA + + "\" not set for request body of type " + + requestBody.getClass().getSimpleName(); throw new EncodeException(message); } } @@ -95,8 +95,8 @@ public class SpringEncoder implements Encoder { if (log.isDebugEnabled()) { if (requestContentType != null) { log.debug("Writing [" + requestBody + "] as \"" - + requestContentType + "\" using [" - + messageConverter + "]"); + + requestContentType + "\" using [" + messageConverter + + "]"); } else { log.debug("Writing [" + requestBody + "] using [" @@ -124,14 +124,17 @@ public class SpringEncoder implements Encoder { Charset charset; if (messageConverter instanceof ByteArrayHttpMessageConverter) { charset = null; - } else if (messageConverter instanceof ProtobufHttpMessageConverter && - ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith(outputMessage.getHeaders().getContentType())) { + } + else if (messageConverter instanceof ProtobufHttpMessageConverter + && ProtobufHttpMessageConverter.PROTOBUF.isCompatibleWith( + outputMessage.getHeaders().getContentType())) { charset = null; - } else { + } + else { charset = StandardCharsets.UTF_8; } - request.body(Request.Body.encoded(outputMessage.getOutputStream() - .toByteArray(), charset)); + request.body(Request.Body.encoded( + outputMessage.getOutputStream().toByteArray(), charset)); return; } } @@ -144,14 +147,14 @@ public class SpringEncoder implements Encoder { } } - private class FeignOutputMessage implements HttpOutputMessage { + private final class FeignOutputMessage implements HttpOutputMessage { private final ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); private final HttpHeaders httpHeaders; private FeignOutputMessage(RequestTemplate request) { - httpHeaders = getHttpHeaders(request.headers()); + this.httpHeaders = getHttpHeaders(request.headers()); } @Override diff --git a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java index 8f74232b..3c7761cf 100644 --- a/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java +++ b/spring-cloud-openfeign-core/src/main/java/org/springframework/cloud/openfeign/support/SpringMvcContract.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -75,18 +75,22 @@ public class SpringMvcContract extends Contract.BaseContract private static final String CONTENT_TYPE = "Content-Type"; - private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = - TypeDescriptor.valueOf(String.class); - private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR = - TypeDescriptor.valueOf(Iterable.class); + private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor + .valueOf(String.class); + + private static final TypeDescriptor ITERABLE_TYPE_DESCRIPTOR = TypeDescriptor + .valueOf(Iterable.class); private static final ParameterNameDiscoverer PARAMETER_NAME_DISCOVERER = new DefaultParameterNameDiscoverer(); private final Map, AnnotatedParameterProcessor> annotatedArgumentProcessors; + private final Map processedMethods = new HashMap<>(); private final ConversionService conversionService; + private final ConvertingExpanderFactory convertingExpanderFactory; + private ResourceLoader resourceLoader = new DefaultResourceLoader(); public SpringMvcContract() { @@ -117,6 +121,26 @@ public class SpringMvcContract extends Contract.BaseContract this.convertingExpanderFactory = new ConvertingExpanderFactory(conversionService); } + private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) { + Parameter parameter = method.getParameters()[paramIndex]; + MethodParameter methodParameter = MethodParameter.forParameter(parameter); + TypeDescriptor typeDescriptor = new TypeDescriptor(methodParameter); + + // Feign applies the Param.Expander to each element of an Iterable, so in those + // cases we need to provide a TypeDescriptor of the element. + if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) { + TypeDescriptor elementTypeDescriptor = typeDescriptor + .getElementTypeDescriptor(); + + checkState(elementTypeDescriptor != null, + "Could not resolve element type of Iterable type %s. Not declared?", + typeDescriptor); + + typeDescriptor = elementTypeDescriptor; + } + return typeDescriptor; + } + @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; @@ -190,8 +214,7 @@ public class SpringMvcContract extends Contract.BaseContract if (pathValue != null) { pathValue = resolve(pathValue); // Append path from @RequestMapping if value is present on method - if (!pathValue.startsWith("/") - && !data.template().path().endsWith("/")) { + if (!pathValue.startsWith("/") && !data.template().path().endsWith("/")) { pathValue = "/" + pathValue; } data.template().uri(pathValue, true); @@ -256,9 +279,10 @@ public class SpringMvcContract extends Contract.BaseContract if (isHttpAnnotation && data.indexToExpander().get(paramIndex) == null) { TypeDescriptor typeDescriptor = createTypeDescriptor(method, paramIndex); - if (conversionService.canConvert(typeDescriptor, STRING_TYPE_DESCRIPTOR)) { - Param.Expander expander = - convertingExpanderFactory.getExpander(typeDescriptor); + if (this.conversionService.canConvert(typeDescriptor, + STRING_TYPE_DESCRIPTOR)) { + Param.Expander expander = this.convertingExpanderFactory + .getExpander(typeDescriptor); if (expander != null) { data.indexToExpander().put(paramIndex, expander); } @@ -267,26 +291,6 @@ public class SpringMvcContract extends Contract.BaseContract return isHttpAnnotation; } - private static TypeDescriptor createTypeDescriptor(Method method, int paramIndex) { - Parameter parameter = method.getParameters()[paramIndex]; - MethodParameter methodParameter = MethodParameter.forParameter(parameter); - TypeDescriptor typeDescriptor = new TypeDescriptor(methodParameter); - - // Feign applies the Param.Expander to each element of an Iterable, so in those - // cases we need to provide a TypeDescriptor of the element. - if (typeDescriptor.isAssignableTo(ITERABLE_TYPE_DESCRIPTOR)) { - TypeDescriptor elementTypeDescriptor = - typeDescriptor.getElementTypeDescriptor(); - - checkState(elementTypeDescriptor != null, - "Could not resolve element type of Iterable type %s. Not declared?", - typeDescriptor); - - typeDescriptor = elementTypeDescriptor; - } - return typeDescriptor; - } - private void parseProduces(MethodMetadata md, Method method, RequestMapping annotation) { String[] serverProduces = annotation.produces(); @@ -315,7 +319,7 @@ public class SpringMvcContract extends Contract.BaseContract int index = header.indexOf('='); if (!header.contains("!=") && index >= 0) { md.template().header(resolve(header.substring(0, index)), - resolve(header.substring(index + 1).trim())); + resolve(header.substring(index + 1).trim())); } } } @@ -360,48 +364,14 @@ public class SpringMvcContract extends Contract.BaseContract parameterAnnotation.annotationType(), null); } - private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes, String[] parameterNames) { + private boolean shouldAddParameterName(int parameterIndex, Type[] parameterTypes, + String[] parameterNames) { // has a parameter name return parameterNames != null && parameterNames.length > parameterIndex - // has a type + // has a type && parameterTypes != null && parameterTypes.length > parameterIndex; } - private class SimpleAnnotatedParameterContext - implements AnnotatedParameterProcessor.AnnotatedParameterContext { - - private final MethodMetadata methodMetadata; - - private final int parameterIndex; - - public SimpleAnnotatedParameterContext(MethodMetadata methodMetadata, - int parameterIndex) { - this.methodMetadata = methodMetadata; - this.parameterIndex = parameterIndex; - } - - @Override - public MethodMetadata getMethodMetadata() { - return this.methodMetadata; - } - - @Override - public int getParameterIndex() { - return this.parameterIndex; - } - - @Override - public void setParameterName(String name) { - nameParam(this.methodMetadata, name, this.parameterIndex); - } - - @Override - public Collection setTemplateParameter(String name, - Collection rest) { - return addTemplateParameter(rest, name); - } - } - /** * @deprecated Not used internally anymore. Will be removed in the future. */ @@ -431,10 +401,48 @@ public class SpringMvcContract extends Contract.BaseContract Param.Expander getExpander(TypeDescriptor typeDescriptor) { return value -> { - Object converted = this.conversionService.convert( - value, typeDescriptor, STRING_TYPE_DESCRIPTOR); + Object converted = this.conversionService.convert(value, typeDescriptor, + STRING_TYPE_DESCRIPTOR); return (String) converted; }; } + } + + private class SimpleAnnotatedParameterContext + implements AnnotatedParameterProcessor.AnnotatedParameterContext { + + private final MethodMetadata methodMetadata; + + private final int parameterIndex; + + SimpleAnnotatedParameterContext(MethodMetadata methodMetadata, + int parameterIndex) { + this.methodMetadata = methodMetadata; + this.parameterIndex = parameterIndex; + } + + @Override + public MethodMetadata getMethodMetadata() { + return this.methodMetadata; + } + + @Override + public int getParameterIndex() { + return this.parameterIndex; + } + + @Override + public void setParameterName(String name) { + nameParam(this.methodMetadata, name, this.parameterIndex); + } + + @Override + public Collection setTemplateParameter(String name, + Collection rest) { + return addTemplateParameter(rest, name); + } + + } + } diff --git a/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json index 9a6ed67a..fc025c9d 100644 --- a/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json +++ b/spring-cloud-openfeign-core/src/main/resources/META-INF/additional-spring-configuration-metadata.json @@ -1,33 +1,36 @@ -{"groups": [ -], "properties": [ - { - "name": "feign.hystrix.enabled", - "type": "java.lang.Boolean", - "description": "If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.", - "defaultValue": "false" - }, - { - "name": "feign.httpclient.enabled", - "type": "java.lang.Boolean", - "description": "Enables the use of the Apache HTTP Client by Feign.", - "defaultValue": "true" - }, - { - "name": "feign.okhttp.enabled", - "type": "java.lang.Boolean", - "description": "Enables the use of the OK HTTP Client by Feign.", - "defaultValue": "false" - }, - { - "name": "feign.compression.response.enabled", - "type": "java.lang.Boolean", - "description": "Enables the response from Feign to be compressed.", - "defaultValue": "false" - }, - { - "name": "feign.compression.request.enabled", - "type": "java.lang.Boolean", - "description": "Enables the request sent by Feign to be compressed.", - "defaultValue": "false" - } -]} +{ + "groups": [ + ], + "properties": [ + { + "name": "feign.hystrix.enabled", + "type": "java.lang.Boolean", + "description": "If true, an OpenFeign client will be wrapped with a Hystrix circuit breaker.", + "defaultValue": "false" + }, + { + "name": "feign.httpclient.enabled", + "type": "java.lang.Boolean", + "description": "Enables the use of the Apache HTTP Client by Feign.", + "defaultValue": "true" + }, + { + "name": "feign.okhttp.enabled", + "type": "java.lang.Boolean", + "description": "Enables the use of the OK HTTP Client by Feign.", + "defaultValue": "false" + }, + { + "name": "feign.compression.response.enabled", + "type": "java.lang.Boolean", + "description": "Enables the response from Feign to be compressed.", + "defaultValue": "false" + }, + { + "name": "feign.compression.request.enabled", + "type": "java.lang.Boolean", + "description": "Enables the request sent by Feign to be compressed.", + "defaultValue": "false" + } + ] +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/EnableFeignClientsTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/EnableFeignClientsTests.java index 75cfeee3..77f5dbb8 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/EnableFeignClientsTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/EnableFeignClientsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,13 +12,20 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; +import feign.Contract; +import feign.Feign; +import feign.Logger; +import feign.codec.Decoder; +import feign.codec.Encoder; +import feign.optionals.OptionalDecoder; +import feign.slf4j.Slf4jLogger; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -30,14 +37,6 @@ import org.springframework.context.annotation.Import; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import feign.Contract; -import feign.Feign; -import feign.Logger; -import feign.codec.Decoder; -import feign.codec.Encoder; -import feign.optionals.OptionalDecoder; -import feign.slf4j.Slf4jLogger; - /** * @author Spencer Gibb */ @@ -51,8 +50,7 @@ public class EnableFeignClientsTests { @Test public void decoderDefaultCorrect() { - OptionalDecoder.class - .cast(this.feignContext.getInstance("foo", Decoder.class)); + OptionalDecoder.class.cast(this.feignContext.getInstance("foo", Decoder.class)); } @Test @@ -81,6 +79,7 @@ public class EnableFeignClientsTests { @Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class }) protected static class PlainConfiguration { + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientBuilderTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientBuilderTests.java index 1555eb9b..07637be5 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientBuilderTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientBuilderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,16 +25,18 @@ import java.util.Collections; import java.util.List; import org.hamcrest.Matchers; -import org.junit.Assert; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.mockito.Mockito; + import org.springframework.cloud.openfeign.testclients.TestClient; import org.springframework.context.ApplicationContext; import org.springframework.util.ReflectionUtils; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Sven Döring */ @@ -65,14 +67,14 @@ public class FeignClientBuilderTests { fieldName); ReflectionUtils.makeAccessible(field); final Object value = ReflectionUtils.getField(field, factoryBean); - Assert.assertEquals("Expected value for the field '" + fieldName + "':", - expectedValue, value); + assertThat(value).as("Expected value for the field '" + fieldName + "':") + .isEqualTo(expectedValue); } @Before public void setUp() { this.applicationContext = Mockito.mock(ApplicationContext.class); - this.feignClientBuilder = new FeignClientBuilder(applicationContext); + this.feignClientBuilder = new FeignClientBuilder(this.applicationContext); } @Test @@ -91,18 +93,18 @@ public class FeignClientBuilderTests { // on this builder class. // (2) Or a new field was added and the builder class has to be extended with this // new field. - Assert.assertThat(methodNames, Matchers.contains("contextId", "decode404", "fallback", - "fallbackFactory", "name", "path", "url")); + assertThat(methodNames).containsExactly("contextId", "decode404", "fallback", + "fallbackFactory", "name", "path", "url"); } @Test public void forType_preinitializedBuilder() { // when: - final FeignClientBuilder.Builder builder = feignClientBuilder + final FeignClientBuilder.Builder builder = this.feignClientBuilder .forType(FeignClientBuilderTests.class, "TestClient"); // then: - assertFactoryBeanField(builder, "applicationContext", applicationContext); + assertFactoryBeanField(builder, "applicationContext", this.applicationContext); assertFactoryBeanField(builder, "type", FeignClientBuilderTests.class); assertFactoryBeanField(builder, "name", "TestClient"); assertFactoryBeanField(builder, "contextId", "TestClient"); @@ -123,13 +125,13 @@ public class FeignClientBuilderTests { @Test public void forType_allFieldsSetOnBuilder() { // when: - final FeignClientBuilder.Builder builder = feignClientBuilder + final FeignClientBuilder.Builder builder = this.feignClientBuilder .forType(FeignClientBuilderTests.class, "TestClient").decode404(true) .fallback(Object.class).fallbackFactory(Object.class).path("Path/") .url("Url/"); // then: - assertFactoryBeanField(builder, "applicationContext", applicationContext); + assertFactoryBeanField(builder, "applicationContext", this.applicationContext); assertFactoryBeanField(builder, "type", FeignClientBuilderTests.class); assertFactoryBeanField(builder, "name", "TestClient"); @@ -144,16 +146,17 @@ public class FeignClientBuilderTests { @Test public void forType_build() { // given: - Mockito.when(applicationContext.getBean(FeignContext.class)) + Mockito.when(this.applicationContext.getBean(FeignContext.class)) .thenThrow(new ClosedFileSystemException()); // throw an unusual exception // in the // FeignClientFactoryBean - final FeignClientBuilder.Builder builder = feignClientBuilder + final FeignClientBuilder.Builder builder = this.feignClientBuilder .forType(TestClient.class, "TestClient"); // expect: 'the build will fail right after calling build() with the mocked // unusual exception' - thrown.expect(Matchers.isA(ClosedFileSystemException.class)); + this.thrown.expect(Matchers.isA(ClosedFileSystemException.class)); builder.build(); } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientFactoryTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientFactoryTests.java index 48ecbee9..c2559dee 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientFactoryTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,16 +16,14 @@ package org.springframework.cloud.openfeign; +import java.util.Arrays; + import org.junit.Test; + import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; -import java.util.Arrays; - -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb @@ -42,33 +40,43 @@ public class FeignClientFactoryTests { getSpec("bar", BarConfig.class))); Foo foo = context.getInstance("foo", Foo.class); - assertThat("foo was null", foo, is(notNullValue())); + assertThat(foo).as("foo was null").isNotNull(); Bar bar = context.getInstance("bar", Bar.class); - assertThat("bar was null", bar, is(notNullValue())); + assertThat(bar).as("bar was null").isNotNull(); Bar foobar = context.getInstance("foo", Bar.class); - assertThat("bar was not null", foobar, is(nullValue())); + assertThat(foobar).as("bar was not null").isNull(); } private FeignClientSpecification getSpec(String name, Class configClass) { - return new FeignClientSpecification(name, new Class[]{configClass}); + return new FeignClientSpecification(name, new Class[] { configClass }); } static class FooConfig { + @Bean Foo foo() { return new Foo(); } } - static class Foo{} + + static class Foo { + + } static class BarConfig { + @Bean Bar bar() { return new Bar(); } + } - static class Bar{} + + static class Bar { + + } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientOverrideDefaultsTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientOverrideDefaultsTests.java index 844bda57..aa3a86e4 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientOverrideDefaultsTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientOverrideDefaultsTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,31 +12,10 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration; -import org.springframework.cloud.openfeign.support.SpringEncoder; -import org.springframework.cloud.openfeign.support.SpringMvcContract; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Import; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestMethod; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; - import feign.Contract; import feign.Feign; import feign.Logger; @@ -52,6 +31,24 @@ import feign.codec.ErrorDecoder; import feign.hystrix.HystrixFeign; import feign.optionals.OptionalDecoder; import feign.slf4j.Slf4jLogger; +import org.junit.Test; +import org.junit.runner.RunWith; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.netflix.archaius.ArchaiusAutoConfiguration; +import org.springframework.cloud.openfeign.support.SpringEncoder; +import org.springframework.cloud.openfeign.support.SpringMvcContract; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.test.annotation.DirtiesContext; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; + +import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb @@ -72,8 +69,8 @@ public class FeignClientOverrideDefaultsTests { @Test public void clientsAvailable() { - assertNotNull(this.foo); - assertNotNull(this.bar); + assertThat(this.foo).isNotNull(); + assertThat(this.bar).isNotNull(); } @Test @@ -102,45 +99,62 @@ public class FeignClientOverrideDefaultsTests { @Test public void overrideLoggerLevel() { - assertNull(this.context.getInstance("foo", Logger.Level.class)); - assertEquals(Logger.Level.HEADERS, - this.context.getInstance("bar", Logger.Level.class)); + assertThat(this.context.getInstance("foo", Logger.Level.class)).isNull(); + assertThat(this.context.getInstance("bar", Logger.Level.class)) + .isEqualTo(Logger.Level.HEADERS); } @Test public void overrideRetryer() { - assertEquals(Retryer.NEVER_RETRY, this.context.getInstance("foo", Retryer.class)); + assertThat(this.context.getInstance("foo", Retryer.class)) + .isEqualTo(Retryer.NEVER_RETRY); Retryer.Default.class.cast(this.context.getInstance("bar", Retryer.class)); } @Test public void overrideErrorDecoder() { - assertNull(this.context.getInstance("foo", ErrorDecoder.class)); + assertThat(this.context.getInstance("foo", ErrorDecoder.class)).isNull(); ErrorDecoder.Default.class .cast(this.context.getInstance("bar", ErrorDecoder.class)); } @Test public void overrideBuilder() { - HystrixFeign.Builder.class.cast(this.context.getInstance("foo", Feign.Builder.class)); - Feign.Builder.class - .cast(this.context.getInstance("bar", Feign.Builder.class)); + HystrixFeign.Builder.class + .cast(this.context.getInstance("foo", Feign.Builder.class)); + Feign.Builder.class.cast(this.context.getInstance("bar", Feign.Builder.class)); } @Test public void overrideRequestOptions() { - assertNull(this.context.getInstance("foo", Request.Options.class)); + assertThat(this.context.getInstance("foo", Request.Options.class)).isNull(); Request.Options options = this.context.getInstance("bar", Request.Options.class); - assertEquals(1, options.connectTimeoutMillis()); - assertEquals(1, options.readTimeoutMillis()); + assertThat(options.connectTimeoutMillis()).isEqualTo(1); + assertThat(options.readTimeoutMillis()).isEqualTo(1); } @Test public void addRequestInterceptor() { - assertEquals(1, - this.context.getInstances("foo", RequestInterceptor.class).size()); - assertEquals(2, - this.context.getInstances("bar", RequestInterceptor.class).size()); + assertThat(this.context.getInstances("foo", RequestInterceptor.class).size()) + .isEqualTo(1); + assertThat(this.context.getInstances("bar", RequestInterceptor.class).size()) + .isEqualTo(2); + } + + @FeignClient(name = "foo", url = "http://foo", configuration = FooConfiguration.class) + interface FooClient { + + @RequestLine("GET /") + String get(); + + } + + @FeignClient(name = "bar", url = "http://bar", configuration = BarConfiguration.class) + interface BarClient { + + @RequestMapping(value = "/", method = RequestMethod.GET) + String get(); + } @Configuration @@ -148,6 +162,7 @@ public class FeignClientOverrideDefaultsTests { @Import({ PropertyPlaceholderAutoConfiguration.class, ArchaiusAutoConfiguration.class, FeignAutoConfiguration.class }) protected static class TestConfiguration { + @Bean RequestInterceptor defaultRequestInterceptor() { return new RequestInterceptor() { @@ -156,16 +171,11 @@ public class FeignClientOverrideDefaultsTests { } }; } - } - - @FeignClient(name = "foo", url = "http://foo", configuration = FooConfiguration.class) - interface FooClient { - @RequestLine("GET /") - String get(); } public static class FooConfiguration { + @Bean public Decoder feignDecoder() { return new Decoder.Default(); @@ -190,15 +200,11 @@ public class FeignClientOverrideDefaultsTests { public Feign.Builder feignBuilder() { return HystrixFeign.builder(); } - } - @FeignClient(name = "bar", url = "http://bar", configuration = BarConfiguration.class) - interface BarClient { - @RequestMapping(value = "/", method = RequestMethod.GET) - String get(); } public static class BarConfiguration { + @Bean Logger.Level feignLevel() { return Logger.Level.HEADERS; @@ -223,5 +229,7 @@ public class FeignClientOverrideDefaultsTests { RequestInterceptor feignRequestInterceptor() { return new BasicAuthRequestInterceptor("user", "pass"); } + } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java index 06ebd16f..26ea9e31 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientUsingPropertiesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -37,7 +37,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; @@ -52,14 +51,15 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.fail; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * @author Eko Kurniawan Khannedy */ @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT) +@SpringBootTest(classes = FeignClientUsingPropertiesTests.Application.class, webEnvironment = RANDOM_PORT) @TestPropertySource("classpath:feign-properties.properties") @DirtiesContext public class FeignClientUsingPropertiesTests { @@ -80,38 +80,41 @@ public class FeignClientUsingPropertiesTests { private FeignClientFactoryBean formFactoryBean; public FeignClientUsingPropertiesTests() { - fooFactoryBean = new FeignClientFactoryBean(); - fooFactoryBean.setContextId("foo"); - fooFactoryBean.setType(FeignClientFactoryBean.class); + this.fooFactoryBean = new FeignClientFactoryBean(); + this.fooFactoryBean.setContextId("foo"); + this.fooFactoryBean.setType(FeignClientFactoryBean.class); - barFactoryBean = new FeignClientFactoryBean(); - barFactoryBean.setContextId("bar"); - barFactoryBean.setType(FeignClientFactoryBean.class); + this.barFactoryBean = new FeignClientFactoryBean(); + this.barFactoryBean.setContextId("bar"); + this.barFactoryBean.setType(FeignClientFactoryBean.class); - formFactoryBean = new FeignClientFactoryBean(); - formFactoryBean.setContextId("form"); - formFactoryBean.setType(FeignClientFactoryBean.class); + this.formFactoryBean = new FeignClientFactoryBean(); + this.formFactoryBean.setContextId("form"); + this.formFactoryBean.setType(FeignClientFactoryBean.class); } public FooClient fooClient() { - fooFactoryBean.setApplicationContext(applicationContext); - return fooFactoryBean.feign(context).target(FooClient.class, "http://localhost:" + this.port); + this.fooFactoryBean.setApplicationContext(this.applicationContext); + return this.fooFactoryBean.feign(this.context).target(FooClient.class, + "http://localhost:" + this.port); } public BarClient barClient() { - barFactoryBean.setApplicationContext(applicationContext); - return barFactoryBean.feign(context).target(BarClient.class, "http://localhost:" + this.port); + this.barFactoryBean.setApplicationContext(this.applicationContext); + return this.barFactoryBean.feign(this.context).target(BarClient.class, + "http://localhost:" + this.port); } public FormClient formClient() { - formFactoryBean.setApplicationContext(applicationContext); - return formFactoryBean.feign(context).target(FormClient.class, "http://localhost:" + this.port); + this.formFactoryBean.setApplicationContext(this.applicationContext); + return this.formFactoryBean.feign(this.context).target(FormClient.class, + "http://localhost:" + this.port); } @Test public void testFoo() { String response = fooClient().foo(); - assertEquals("OK", response); + assertThat(response).isEqualTo("OK"); } @Test(expected = RetryableException.class) @@ -124,25 +127,26 @@ public class FeignClientUsingPropertiesTests { public void testForm() { Map request = Collections.singletonMap("form", "Data"); String response = formClient().form(request); - assertEquals("Data", response); + assertThat(response).isEqualTo("Data"); } protected interface FooClient { @RequestMapping(method = RequestMethod.GET, value = "/foo") String foo(); + } protected interface BarClient { @RequestMapping(method = RequestMethod.GET, value = "/bar") String bar(); + } protected interface FormClient { - @RequestMapping(value = "/form", method = RequestMethod.POST, - consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @RequestMapping(value = "/form", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) String form(Map form); } @@ -155,10 +159,11 @@ public class FeignClientUsingPropertiesTests { @RequestMapping(method = RequestMethod.GET, value = "/foo") public String foo(HttpServletRequest request) throws IllegalAccessException { - if ("Foo".equals(request.getHeader("Foo")) && - "Bar".equals(request.getHeader("Bar"))) { + if ("Foo".equals(request.getHeader("Foo")) + && "Bar".equals(request.getHeader("Bar"))) { return "OK"; - } else { + } + else { throw new IllegalAccessException("It should has Foo and Bar header"); } } @@ -169,8 +174,7 @@ public class FeignClientUsingPropertiesTests { return "OK"; } - @RequestMapping(value = "/form", method = RequestMethod.POST, - consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) + @RequestMapping(value = "/form", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE) public String form(HttpServletRequest request) { return request.getParameter("form"); } @@ -178,17 +182,21 @@ public class FeignClientUsingPropertiesTests { } public static class FooRequestInterceptor implements RequestInterceptor { + @Override public void apply(RequestTemplate template) { template.header("Foo", "Foo"); } + } public static class BarRequestInterceptor implements RequestInterceptor { + @Override public void apply(RequestTemplate template) { template.header("Bar", "Bar"); } + } public static class NoRetryer implements Retryer { @@ -202,24 +210,29 @@ public class FeignClientUsingPropertiesTests { public Retryer clone() { return this; } + } public static class DefaultErrorDecoder extends ErrorDecoder.Default { + } public static class FormEncoder implements Encoder { @Override - public void encode(Object o, Type type, RequestTemplate requestTemplate) throws EncodeException { + public void encode(Object o, Type type, RequestTemplate requestTemplate) + throws EncodeException { Map form = (Map) o; StringBuilder builder = new StringBuilder(); form.forEach((key, value) -> { builder.append(key + "=" + value + "&"); }); - requestTemplate.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED_VALUE); + requestTemplate.header(HttpHeaders.CONTENT_TYPE, + MediaType.APPLICATION_FORM_URLENCODED_VALUE); requestTemplate.body(Request.Body.bodyTemplate(builder.toString(), UTF_8)); } + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java index de6bf56d..e5a41425 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignClientsRegistrarTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; @@ -20,6 +19,7 @@ package org.springframework.cloud.openfeign; import java.util.Collections; import org.junit.Test; + import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; @@ -27,8 +27,7 @@ import org.springframework.mock.env.MockEnvironment; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb @@ -41,7 +40,6 @@ public class FeignClientsRegistrarTests { testGetName("http://bad_hostname"); } - @Test(expected = IllegalStateException.class) public void badNameHttpsPrefix() { testGetName("https://bad_hostname"); @@ -60,19 +58,19 @@ public class FeignClientsRegistrarTests { @Test public void goodName() { String name = testGetName("good-name"); - assertThat("name was wrong", name, is("good-name")); + assertThat(name).as("name was wrong").isEqualTo("good-name"); } @Test public void goodNameHttpPrefix() { String name = testGetName("http://good-name"); - assertThat("name was wrong", name, is("http://good-name")); + assertThat(name).as("name was wrong").isEqualTo("http://good-name"); } @Test public void goodNameHttpsPrefix() { String name = testGetName("https://goodname"); - assertThat("name was wrong", name, is("https://goodname")); + assertThat(name).as("name was wrong").isEqualTo("https://goodname"); } private String testGetName(String name) { @@ -81,7 +79,6 @@ public class FeignClientsRegistrarTests { return registrar.getName(Collections.singletonMap("name", name)); } - @Test(expected = IllegalArgumentException.class) public void testFallback() { new AnnotationConfigApplicationContext(FallbackTestConfig.class); @@ -92,30 +89,35 @@ public class FeignClientsRegistrarTests { new AnnotationConfigApplicationContext(FallbackFactoryTestConfig.class); } - @Configuration - @EnableAutoConfiguration - @EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class}) - protected static class FallbackTestConfig { - - } - @FeignClient(name = "fallbackTestClient", url = "http://localhost:8080/", fallback = FallbackClient.class) protected interface FallbackClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") String fallbackTest(); - } - - @Configuration - @EnableAutoConfiguration - @EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackFactoryClient.class}) - protected static class FallbackFactoryTestConfig { } @FeignClient(name = "fallbackFactoryTestClient", url = "http://localhost:8081/", fallbackFactory = FallbackFactoryClient.class) protected interface FallbackFactoryClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") String fallbackFactoryTest(); + + } + + @Configuration + @EnableAutoConfiguration + @EnableFeignClients(clients = { FeignClientsRegistrarTests.FallbackClient.class }) + protected static class FallbackTestConfig { + + } + + @Configuration + @EnableAutoConfiguration + @EnableFeignClients(clients = { + FeignClientsRegistrarTests.FallbackFactoryClient.class }) + protected static class FallbackFactoryTestConfig { + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignCompressionTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignCompressionTests.java index afcf3269..1feeca48 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignCompressionTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignCompressionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,15 +12,18 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; import java.util.Map; +import feign.Client; +import feign.RequestInterceptor; +import feign.httpclient.ApacheHttpClient; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.runner.ApplicationContextRunner; @@ -35,10 +38,6 @@ import org.springframework.cloud.test.ModifiedClassPathRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import feign.Client; -import feign.RequestInterceptor; -import feign.httpclient.ApacheHttpClient; - import static org.assertj.core.api.Assertions.assertThat; /** @@ -90,7 +89,9 @@ public class FeignCompressionTests { * a bean here of type ApacheHttpClient so that the configuration will be * loaded correctly. */ - return (ApacheHttpClient) client; + return (ApacheHttpClient) this.client; } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClientConfigurationTests.java index ea8a557a..e489655e 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClientConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClientConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,14 +12,16 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.openfeign; import java.lang.reflect.Field; + import javax.net.ssl.SSLContextSpi; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; + import org.apache.http.config.Lookup; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; @@ -28,6 +30,7 @@ import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; @@ -36,8 +39,7 @@ import org.springframework.cloud.test.ModifiedClassPathRunner; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.util.ReflectionUtils; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Ryan Baxter @@ -50,8 +52,8 @@ public class FeignHttpClientConfigurationTests { @Before public void setUp() { - context = new SpringApplicationBuilder() - .properties("debug=true","feign.httpclient.disableSslValidation=true") + this.context = new SpringApplicationBuilder() + .properties("debug=true", "feign.httpclient.disableSslValidation=true") .web(WebApplicationType.NONE) .sources(HttpClientConfiguration.class, FeignAutoConfiguration.class) .run(); @@ -59,29 +61,38 @@ public class FeignHttpClientConfigurationTests { @After public void tearDown() { - if(context != null) { - context.close(); + if (this.context != null) { + this.context.close(); } } @Test public void disableSslTest() throws Exception { - HttpClientConnectionManager connectionManager = context.getBean(HttpClientConnectionManager.class); - Lookup socketFactoryRegistry = getConnectionSocketFactoryLookup(connectionManager); - assertNotNull(socketFactoryRegistry.lookup("https")); - assertNull(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers()); + HttpClientConnectionManager connectionManager = this.context + .getBean(HttpClientConnectionManager.class); + Lookup socketFactoryRegistry = getConnectionSocketFactoryLookup( + connectionManager); + assertThat(socketFactoryRegistry.lookup("https")).isNotNull(); + assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers()) + .isNull(); } - private Lookup getConnectionSocketFactoryLookup(HttpClientConnectionManager connectionManager) { - DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator)this.getField(connectionManager, "connectionOperator"); - return (Lookup)this.getField(connectionOperator, "socketFactoryRegistry"); + private Lookup getConnectionSocketFactoryLookup( + HttpClientConnectionManager connectionManager) { + DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) this + .getField(connectionManager, "connectionOperator"); + return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry"); } - private X509TrustManager getX509TrustManager(Lookup socketFactoryRegistry) { - ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory)socketFactoryRegistry.lookup("https"); - SSLSocketFactory sslSocketFactory = (SSLSocketFactory)this.getField(connectionSocketFactory, "socketfactory"); - SSLContextSpi sslContext = (SSLContextSpi)this.getField(sslSocketFactory, "context"); - return (X509TrustManager)this.getField(sslContext, "trustManager"); + private X509TrustManager getX509TrustManager( + Lookup socketFactoryRegistry) { + ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory) socketFactoryRegistry + .lookup("https"); + SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this + .getField(connectionSocketFactory, "socketfactory"); + SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory, + "context"); + return (X509TrustManager) this.getField(sslContext, "trustManager"); } protected Object getField(Object target, String name) { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClientUrlTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClientUrlTests.java index 46774566..7e0aaee1 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClientUrlTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignHttpClientUrlTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,28 +12,25 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; - import java.lang.reflect.Field; import java.util.Objects; +import feign.Client; +import feign.Feign; +import feign.Target; +import feign.httpclient.ApacheHttpClient; import org.junit.AfterClass; import org.junit.BeforeClass; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,16 +43,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import feign.Client; -import feign.Feign; -import feign.Target; -import feign.httpclient.ApacheHttpClient; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.DEFINED_PORT; /** * @author Spencer Gibb */ @RunWith(SpringRunner.class) -@SpringBootTest(classes = FeignHttpClientUrlTests.TestConfig.class, webEnvironment = WebEnvironment.DEFINED_PORT, value = { +@SpringBootTest(classes = FeignHttpClientUrlTests.TestConfig.class, webEnvironment = DEFINED_PORT, value = { "spring.application.name=feignclienturltest", "feign.hystrix.enabled=false", "feign.okhttp.enabled=false" }) @DirtiesContext @@ -63,6 +58,15 @@ public class FeignHttpClientUrlTests { static int port; + @Autowired + BeanUrlClientNoProtocol beanClientNoProtocol; + + @Autowired + private UrlClient urlClient; + + @Autowired + private BeanUrlClient beanClient; + @BeforeClass public static void beforeClass() { port = SocketUtils.findAvailableTcpPort(); @@ -74,37 +78,61 @@ public class FeignHttpClientUrlTests { System.clearProperty("server.port"); } - @Autowired - private UrlClient urlClient; + @Test + public void testUrlHttpClient() { + assertThat(this.urlClient).as("UrlClient was null").isNotNull(); + Hello hello = this.urlClient.getHello(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello("hello world 1")); + } - @Autowired - private BeanUrlClient beanClient; + @Test + public void testBeanUrl() { + Hello hello = this.beanClient.getHello(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello("hello world 1")); + } - @Autowired BeanUrlClientNoProtocol beanClientNoProtocol; + @Test + public void testBeanUrlNoProtocol() { + Hello hello = this.beanClientNoProtocol.getHello(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello("hello world 1")); + } // this tests that @FeignClient(name = "localappurl", url = "http://localhost:${server.port}/") protected interface UrlClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello getHello(); + } @FeignClient(name = "beanappurl", url = "#{SERVER_URL}path") protected interface BeanUrlClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello getHello(); + } @FeignClient(name = "beanappurlnoprotocol", url = "#{SERVER_URL_NO_PROTOCOL}path") protected interface BeanUrlClientNoProtocol { + @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello getHello(); + } @Configuration @EnableAutoConfiguration @RestController - @EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class, BeanUrlClientNoProtocol.class }) + @EnableFeignClients(clients = { UrlClient.class, BeanUrlClient.class, + BeanUrlClientNoProtocol.class }) @Import(NoSecurityConfiguration.class) protected static class TestConfig { @@ -118,12 +146,12 @@ public class FeignHttpClientUrlTests { return getHello(); } - @Bean(name="SERVER_URL") + @Bean(name = "SERVER_URL") public String serverUrl() { return "http://localhost:" + port + "/"; } - @Bean(name="SERVER_URL_NO_PROTOCOL") + @Bean(name = "SERVER_URL_NO_PROTOCOL") public String serverUrlNoProtocol() { return "localhost:" + port + "/"; } @@ -139,8 +167,8 @@ public class FeignHttpClientUrlTests { ReflectionUtils.makeAccessible(field); Client client = (Client) ReflectionUtils.getField(field, feign); if (target.name().equals("localappurl")) { - assertThat("client was wrong type", client, - is(instanceOf(ApacheHttpClient.class))); + assertThat(client).isInstanceOf(ApacheHttpClient.class) + .as("client was wrong type"); } return feign.target(target); } @@ -149,29 +177,8 @@ public class FeignHttpClientUrlTests { } - @Test - public void testUrlHttpClient() { - assertNotNull("UrlClient was null", this.urlClient); - Hello hello = this.urlClient.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello("hello world 1"), hello); - } - - @Test - public void testBeanUrl() { - Hello hello = this.beanClient.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello("hello world 1"), hello); - } - - @Test - public void testBeanUrlNoProtocol() { - Hello hello = this.beanClientNoProtocol.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello("hello world 1"), hello); - } - public static class Hello { + private String message; public Hello() { @@ -182,7 +189,7 @@ public class FeignHttpClientUrlTests { } public String getMessage() { - return message; + return this.message; } public void setMessage(String message) { @@ -191,15 +198,21 @@ public class FeignHttpClientUrlTests { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Hello that = (Hello) o; - return Objects.equals(message, that.message); + return Objects.equals(this.message, that.message); } @Override public int hashCode() { - return Objects.hash(message); + return Objects.hash(this.message); } + } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignLoggerFactoryTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignLoggerFactoryTests.java index dba5c9ea..03875c4e 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignLoggerFactoryTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignLoggerFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,9 +16,8 @@ package org.springframework.cloud.openfeign; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - +import feign.Logger; +import feign.slf4j.Slf4jLogger; import org.junit.Test; import org.springframework.context.annotation.AnnotationConfigApplicationContext; @@ -26,8 +25,7 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; -import feign.Logger; -import feign.slf4j.Slf4jLogger; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Venil Noronha @@ -36,12 +34,38 @@ public class FeignLoggerFactoryTests { @Test public void testDefaultLogger() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration1.class); + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + SampleConfiguration1.class); FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class); - assertNotNull(loggerFactory); + assertThat(loggerFactory).isNotNull(); Logger logger = loggerFactory.create(Object.class); - assertNotNull(logger); - assertTrue(logger instanceof Slf4jLogger); + assertThat(logger).isNotNull(); + assertThat(logger instanceof Slf4jLogger).isTrue(); + context.close(); + } + + @Test + public void testCustomLogger() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + SampleConfiguration2.class); + FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class); + assertThat(loggerFactory).isNotNull(); + Logger logger = loggerFactory.create(Object.class); + assertThat(logger).isNotNull(); + assertThat(logger instanceof LoggerImpl1).isTrue(); + context.close(); + } + + @Test + public void testCustomLoggerFactory() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + SampleConfiguration3.class); + FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class); + assertThat(loggerFactory).isNotNull(); + assertThat(loggerFactory instanceof LoggerFactoryImpl).isTrue(); + Logger logger = loggerFactory.create(Object.class); + assertThat(logger).isNotNull(); + assertThat(logger instanceof LoggerImpl2).isTrue(); context.close(); } @@ -51,17 +75,6 @@ public class FeignLoggerFactoryTests { } - @Test - public void testCustomLogger() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration2.class); - FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class); - assertNotNull(loggerFactory); - Logger logger = loggerFactory.create(Object.class); - assertNotNull(logger); - assertTrue(logger instanceof LoggerImpl1); - context.close(); - } - @Configuration @Import(FeignClientsConfiguration.class) protected static class SampleConfiguration2 { @@ -82,18 +95,6 @@ public class FeignLoggerFactoryTests { } - @Test - public void testCustomLoggerFactory() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SampleConfiguration3.class); - FeignLoggerFactory loggerFactory = context.getBean(FeignLoggerFactory.class); - assertNotNull(loggerFactory); - assertTrue(loggerFactory instanceof LoggerFactoryImpl); - Logger logger = loggerFactory.create(Object.class); - assertNotNull(logger); - assertTrue(logger instanceof LoggerImpl2); - context.close(); - } - @Configuration @Import(FeignClientsConfiguration.class) protected static class SampleConfiguration3 { diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java index 7f4e5450..bb0f4f23 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/FeignOkHttpConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,19 +12,20 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.openfeign; -import okhttp3.OkHttpClient; - import java.lang.reflect.Field; + import javax.net.ssl.HostnameVerifier; + +import okhttp3.OkHttpClient; import org.junit.After; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; @@ -34,6 +35,8 @@ import org.springframework.cloud.test.ModifiedClassPathRunner; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.util.ReflectionUtils; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Ryan Baxter */ @@ -45,23 +48,29 @@ public class FeignOkHttpConfigurationTests { @Before public void setUp() { - context = new SpringApplicationBuilder().properties("debug=true","feign.httpclient.disableSslValidation=true", - "feign.okhttp.enabled=true", "feign.httpclient.enabled=false").web(WebApplicationType.NONE) - .sources(HttpClientConfiguration.class, FeignAutoConfiguration.class).run(); + this.context = new SpringApplicationBuilder() + .properties("debug=true", "feign.httpclient.disableSslValidation=true", + "feign.okhttp.enabled=true", "feign.httpclient.enabled=false") + .web(WebApplicationType.NONE) + .sources(HttpClientConfiguration.class, FeignAutoConfiguration.class) + .run(); } @After public void tearDown() { - if(context != null) { - context.close(); + if (this.context != null) { + this.context.close(); } } @Test public void disableSslTest() throws Exception { - OkHttpClient httpClient = context.getBean(OkHttpClient.class); - HostnameVerifier hostnameVerifier = (HostnameVerifier)this.getField(httpClient, "hostnameVerifier"); - Assert.assertTrue(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier)); + OkHttpClient httpClient = this.context.getBean(OkHttpClient.class); + HostnameVerifier hostnameVerifier = (HostnameVerifier) this.getField(httpClient, + "hostnameVerifier"); + assertThat( + OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier)) + .isTrue(); } protected Object getField(Object target, String name) { @@ -70,4 +79,5 @@ public class FeignOkHttpConfigurationTests { Object value = ReflectionUtils.getField(field, target); return value; } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringDecoderTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringDecoderTests.java index f16c6bb2..9ad1c6be 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringDecoderTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringDecoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import java.util.Objects; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -40,10 +41,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb @@ -72,66 +70,73 @@ public class SpringDecoderTests extends FeignClientFactoryBean { public TestClient testClient(boolean decode404) { setType(this.getClass()); setDecode404(decode404); - return feign(context).target(TestClient.class, "http://localhost:" + this.port); + return feign(this.context).target(TestClient.class, + "http://localhost:" + this.port); } @Test public void testResponseEntity() { ResponseEntity response = testClient().getHelloResponse(); - assertNotNull("response was null", response); - assertEquals("wrong status code", HttpStatus.OK, response.getStatusCode()); + assertThat(response).as("response was null").isNotNull(); + assertThat(response.getStatusCode()).as("wrong status code") + .isEqualTo(HttpStatus.OK); Hello hello = response.getBody(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello("hello world via response"), - hello); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello("hello world via response")); } @Test public void testSimpleType() { Hello hello = testClient().getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello("hello world 1"), hello); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello("hello world 1")); } @Test public void testUserParameterizedTypeDecode() { List hellos = testClient().getHellos(); - assertNotNull("hellos was null", hellos); - assertEquals("hellos was not the right size", 2, hellos.size()); - assertEquals("first hello didn't match", new Hello("hello world 1"), - hellos.get(0)); + assertThat(hellos).as("hellos was null").isNotNull(); + assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2); + assertThat(hellos.get(0)).as("first hello didn't match") + .isEqualTo(new Hello("hello world 1")); } @Test public void testSimpleParameterizedTypeDecode() { List hellos = testClient().getHelloStrings(); - assertNotNull("hellos was null", hellos); - assertEquals("hellos was not the right size", 2, hellos.size()); - assertEquals("first hello didn't match", "hello world 1", hellos.get(0)); + assertThat(hellos).as("hellos was null").isNotNull(); + assertThat(hellos.size()).as("hellos was not the right size").isEqualTo(2); + assertThat(hellos.get(0)).as("first hello didn't match") + .isEqualTo("hello world 1"); } @Test @SuppressWarnings("unchecked") public void testWildcardTypeDecode() { ResponseEntity wildcard = testClient().getWildcard(); - assertNotNull("wildcard was null", wildcard); - assertEquals("wrong status code", HttpStatus.OK, wildcard.getStatusCode()); + assertThat(wildcard).as("wildcard was null").isNotNull(); + assertThat(wildcard.getStatusCode()).as("wrong status code") + .isEqualTo(HttpStatus.OK); Object wildcardBody = wildcard.getBody(); - assertNotNull("wildcardBody was null", wildcardBody); - assertTrue("wildcard not an instance of Map", wildcardBody instanceof Map); + assertThat(wildcardBody).as("wildcardBody was null").isNotNull(); + assertThat(wildcardBody instanceof Map).as("wildcard not an instance of Map") + .isTrue(); Map hello = (Map) wildcardBody; - assertEquals("first hello didn't match", "wildcard", hello.get("message")); + assertThat(hello.get("message")).as("first hello didn't match") + .isEqualTo("wildcard"); } @Test public void testResponseEntityVoid() { ResponseEntity response = testClient().getHelloVoid(); - assertNotNull("response was null", response); + assertThat(response).as("response was null").isNotNull(); List headerVals = response.getHeaders().get("X-test-header"); - assertNotNull("headerVals was null", headerVals); - assertEquals("headerVals size was wrong", 1, headerVals.size()); + assertThat(headerVals).as("headerVals was null").isNotNull(); + assertThat(headerVals.size()).as("headerVals size was wrong").isEqualTo(1); String header = headerVals.get(0); - assertEquals("header was wrong", "myval", header); + assertThat(header).as("header was wrong").isEqualTo("myval"); } @Test(expected = RuntimeException.class) @@ -142,43 +147,12 @@ public class SpringDecoderTests extends FeignClientFactoryBean { @Test public void testDecodes404() { final ResponseEntity response = testClient(true).getNotFound(); - assertNotNull("response was null", response); - assertNull("response body was not null", response.getBody()); - } - - public static class Hello { - private String message; - - public Hello() { - } - - public Hello(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } - - public void setMessage(String message) { - this.message = message; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - Hello that = (Hello) o; - return Objects.equals(message, that.message); - } - - @Override - public int hashCode() { - return Objects.hash(message); - } + assertThat(response).as("response was null").isNotNull(); + assertThat(response.getBody()).as("response body was not null").isNull(); } protected interface TestClient { + @RequestMapping(method = RequestMethod.GET, value = "/helloresponse") ResponseEntity getHelloResponse(); @@ -199,6 +173,45 @@ public class SpringDecoderTests extends FeignClientFactoryBean { @GetMapping("/helloWildcard") ResponseEntity getWildcard(); + + } + + public static class Hello { + + private String message; + + public Hello() { + } + + public Hello(String message) { + this.message = message; + } + + public String getMessage() { + return this.message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Hello that = (Hello) o; + return Objects.equals(this.message, that.message); + } + + @Override + public int hashCode() { + return Objects.hash(this.message); + } + } @Configuration @@ -247,7 +260,7 @@ public class SpringDecoderTests extends FeignClientFactoryBean { public ResponseEntity getWildcard() { return ResponseEntity.ok(new Hello("wildcard")); } - + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringRetryDisabledTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringRetryDisabledTests.java index 43781072..b5f7083e 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringRetryDisabledTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringRetryDisabledTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,62 +14,64 @@ * limitations under the License. */ - package org.springframework.cloud.openfeign; import java.util.Map; + import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.boot.WebApplicationType; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; +import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; +import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration; import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer; import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration; import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer; -import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; -import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration; import org.springframework.cloud.test.ClassPathExclusions; import org.springframework.cloud.test.ModifiedClassPathRunner; import org.springframework.context.ConfigurableApplicationContext; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.hasSize; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.not; -import static org.hamcrest.core.Is.is; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Ryan Baxter */ @RunWith(ModifiedClassPathRunner.class) -@ClassPathExclusions({"spring-retry-*.jar", "spring-boot-starter-aop-*.jar"}) +@ClassPathExclusions({ "spring-retry-*.jar", "spring-boot-starter-aop-*.jar" }) public class SpringRetryDisabledTests { private ConfigurableApplicationContext context; @Before public void setUp() { - context = new SpringApplicationBuilder().web(WebApplicationType.NONE) - .sources(RibbonAutoConfiguration.class, LoadBalancerAutoConfiguration.class, RibbonClientConfiguration.class, - FeignRibbonClientAutoConfiguration.class).run(); + this.context = new SpringApplicationBuilder().web(WebApplicationType.NONE) + .sources(RibbonAutoConfiguration.class, + LoadBalancerAutoConfiguration.class, + RibbonClientConfiguration.class, + FeignRibbonClientAutoConfiguration.class) + .run(); } @After public void tearDown() { - if(context != null) { - context.close(); + if (this.context != null) { + this.context.close(); } } @Test public void testLoadBalancedRetryFactoryBean() throws Exception { - Map lbFactorys = context.getBeansOfType(CachingSpringLoadBalancerFactory.class); - assertThat(lbFactorys.values(), hasSize(1)); - FeignLoadBalancer lb =lbFactorys.values().iterator().next().create("foo"); - assertThat(lb, instanceOf(FeignLoadBalancer.class)); - assertThat(lb, is(not(instanceOf(RetryableFeignLoadBalancer.class)))); + Map lbFactorys = this.context + .getBeansOfType(CachingSpringLoadBalancerFactory.class); + assertThat(lbFactorys.values()).hasSize(1); + FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo"); + assertThat(lb).isInstanceOf(FeignLoadBalancer.class); + assertThat(lb).isNotInstanceOf(RetryableFeignLoadBalancer.class); } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringRetryEnabledTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringRetryEnabledTests.java index 9ab95543..74d699b8 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringRetryEnabledTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/SpringRetryEnabledTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,46 +17,49 @@ package org.springframework.cloud.openfeign; import java.util.Map; -import org.hamcrest.Matchers; + import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.BeansException; import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; +import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; +import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration; import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory; import org.springframework.cloud.openfeign.ribbon.FeignLoadBalancer; import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration; import org.springframework.cloud.openfeign.ribbon.RetryableFeignLoadBalancer; -import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; -import org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Ryan Baxter */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration(classes = {RibbonAutoConfiguration.class, RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class, - FeignRibbonClientAutoConfiguration.class, HttpClientConfiguration.class}) +@ContextConfiguration(classes = { RibbonAutoConfiguration.class, + RibbonClientConfiguration.class, LoadBalancerAutoConfiguration.class, + FeignRibbonClientAutoConfiguration.class, HttpClientConfiguration.class }) public class SpringRetryEnabledTests implements ApplicationContextAware { private ApplicationContext context; @Test public void testLoadBalancedRetryFactoryBean() throws Exception { - Map lbFactorys = context.getBeansOfType(CachingSpringLoadBalancerFactory.class); - assertThat(lbFactorys.values(), Matchers.hasSize(1)); - FeignLoadBalancer lb =lbFactorys.values().iterator().next().create("foo"); - assertThat(lb, instanceOf(RetryableFeignLoadBalancer.class)); + Map lbFactorys = this.context + .getBeansOfType(CachingSpringLoadBalancerFactory.class); + assertThat(lbFactorys.values()).hasSize(1); + FeignLoadBalancer lb = lbFactorys.values().iterator().next().create("foo"); + assertThat(lb).isInstanceOf(RetryableFeignLoadBalancer.class); } @Override public void setApplicationContext(ApplicationContext context) throws BeansException { this.context = context; } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/FeignClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/FeignClientTests.java index ee45e43c..dd29f08b 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/FeignClientTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/FeignClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,7 @@ import java.util.Objects; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; @@ -42,8 +43,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Dave Syer @@ -73,6 +73,44 @@ public class FeignClientTests { @Autowired private TestClient buildByBuilder; + @Test + public void testAnnotations() { + Map beans = this.context + .getBeansWithAnnotation(FeignClient.class); + assertThat(beans.containsKey(TestClient.class.getName())) + .as("Wrong clients: " + beans).isTrue(); + } + + @Test + public void testClient() { + assertThat(this.testClient).as("testClient was null").isNotNull(); + assertThat(this.extraClient).as("extraClient was null").isNotNull(); + assertThat(Proxy.isProxyClass(this.testClient.getClass())) + .as("testClient is not a java Proxy").isTrue(); + InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient); + assertThat(invocationHandler).as("invocationHandler was null").isNotNull(); + } + + @Test + public void extraClient() { + assertThat(this.extraClient).as("extraClient was null").isNotNull(); + assertThat(Proxy.isProxyClass(this.extraClient.getClass())) + .as("extraClient is not a java Proxy").isTrue(); + InvocationHandler invocationHandler = Proxy + .getInvocationHandler(this.extraClient); + assertThat(invocationHandler).as("invocationHandler was null").isNotNull(); + } + + @Test + public void buildByBuilder() { + assertThat(this.buildByBuilder).as("buildByBuilder was null").isNotNull(); + assertThat(Proxy.isProxyClass(this.buildByBuilder.getClass())) + .as("buildByBuilder is not a java Proxy").isTrue(); + InvocationHandler invocationHandler = Proxy + .getInvocationHandler(this.buildByBuilder); + assertThat(invocationHandler).as("invocationHandler was null").isNotNull(); + } + @Configuration @EnableAutoConfiguration @RestController @@ -82,18 +120,18 @@ public class FeignClientTests { @Bean("build-by-builder") public TestClient buildByBuilder(final FeignClientBuilder feignClientBuilder) { - return feignClientBuilder - .forType(TestClient.class, "builderapp") - .build(); + return feignClientBuilder.forType(TestClient.class, "builderapp").build(); } @RequestMapping(method = RequestMethod.GET, value = "/hello") public Hello getHello() { return new Hello("hello world 1"); } + } public static class Hello { + private String message; public Hello() { @@ -104,7 +142,7 @@ public class FeignClientTests { } public String getMessage() { - return message; + return this.message; } public void setMessage(String message) { @@ -113,56 +151,27 @@ public class FeignClientTests { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Hello that = (Hello) o; - return Objects.equals(message, that.message); + return Objects.equals(this.message, that.message); } @Override public int hashCode() { - return message != null ? message.hashCode() : 0; + return this.message != null ? this.message.hashCode() : 0; } - } - @Test - public void testAnnotations() { - Map beans = this.context - .getBeansWithAnnotation(FeignClient.class); - assertTrue("Wrong clients: " + beans, - beans.containsKey(TestClient.class.getName())); - } - - @Test - public void testClient() { - assertNotNull("testClient was null", this.testClient); - assertNotNull("extraClient was null", this.extraClient); - assertTrue("testClient is not a java Proxy", - Proxy.isProxyClass(this.testClient.getClass())); - InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient); - assertNotNull("invocationHandler was null", invocationHandler); - } - - @Test - public void extraClient() { - assertNotNull("extraClient was null", this.extraClient); - assertTrue("extraClient is not a java Proxy", - Proxy.isProxyClass(this.extraClient.getClass())); - InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.extraClient); - assertNotNull("invocationHandler was null", invocationHandler); - } - - @Test - public void buildByBuilder() { - assertNotNull("buildByBuilder was null", this.buildByBuilder); - assertTrue("buildByBuilder is not a java Proxy", - Proxy.isProxyClass(this.buildByBuilder.getClass())); - InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.buildByBuilder); - assertNotNull("invocationHandler was null", invocationHandler); } @Configuration public static class TestDefaultFeignConfig { + } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/TestClient.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/TestClient.java index 7f3ac65c..1322c679 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/TestClient.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/TestClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,8 +23,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @Primary -@FeignClient(value = "localapp") +@FeignClient("localapp") public interface TestClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello getHello(); -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/extra/TestClient.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/extra/TestClient.java index e56e753d..136ed551 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/extra/TestClient.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/beans/extra/TestClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,6 +23,8 @@ import org.springframework.web.bind.annotation.RequestMethod; @FeignClient(value = "otherapp", qualifier = "uniquequalifier") public interface TestClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello getHello(); + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignAcceptEncodingTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignAcceptEncodingTests.java index 732f5787..2eb12f7b 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignAcceptEncodingTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignAcceptEncodingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,7 +22,6 @@ import java.util.List; import com.netflix.loadbalancer.BaseLoadBalancer; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; - import org.junit.Test; import org.junit.runner.RunWith; @@ -30,11 +29,10 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient; import org.springframework.cloud.openfeign.encoding.app.domain.Invoice; -import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -44,15 +42,15 @@ import org.springframework.http.ResponseEntity; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * Tests the response compression. * * @author Jakub Narloch */ -@SpringBootTest(classes = FeignAcceptEncodingTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { +@SpringBootTest(classes = FeignAcceptEncodingTests.Application.class, webEnvironment = RANDOM_PORT, value = { "feign.compression.response.enabled=true" }) @RunWith(SpringRunner.class) @DirtiesContext @@ -68,10 +66,10 @@ public class FeignAcceptEncodingTests { final ResponseEntity> invoices = this.invoiceClient.getInvoices(); // then - assertNotNull(invoices); - assertEquals(HttpStatus.OK, invoices.getStatusCode()); - assertNotNull(invoices.getBody()); - assertEquals(100, invoices.getBody().size()); + assertThat(invoices).isNotNull(); + assertThat(invoices.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(invoices.getBody()).isNotNull(); + assertThat(invoices.getBody().size()).isEqualTo(100); } @@ -80,6 +78,7 @@ public class FeignAcceptEncodingTests { @SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app") @Import(NoSecurityConfiguration.class) public static class Application { + } @Configuration @@ -95,5 +94,7 @@ public class FeignAcceptEncodingTests { Collections.singletonList(new Server("localhost", this.port))); return balancer; } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentEncodingTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentEncodingTests.java index 748b0e6e..22e13b9d 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentEncodingTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/FeignContentEncodingTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,23 +16,23 @@ package org.springframework.cloud.openfeign.encoding; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - import java.util.Collections; import java.util.List; +import com.netflix.loadbalancer.BaseLoadBalancer; +import com.netflix.loadbalancer.ILoadBalancer; +import com.netflix.loadbalancer.Server; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.encoding.app.client.InvoiceClient; import org.springframework.cloud.openfeign.encoding.app.domain.Invoice; -import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -41,16 +41,15 @@ import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import com.netflix.loadbalancer.BaseLoadBalancer; -import com.netflix.loadbalancer.ILoadBalancer; -import com.netflix.loadbalancer.Server; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * Tests the response compression. * * @author Jakub Narloch */ -@SpringBootTest(classes = FeignContentEncodingTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { +@SpringBootTest(classes = FeignContentEncodingTests.Application.class, webEnvironment = RANDOM_PORT, value = { "feign.compression.request.enabled=true", "hystrix.command.default.execution.isolation.strategy=SEMAPHORE", "ribbon.OkToRetryOnAllOperations=false" }) @@ -71,10 +70,10 @@ public class FeignContentEncodingTests { .saveInvoices(invoices); // then - assertNotNull(response); - assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNotNull(response.getBody()); - assertEquals(invoices.size(), response.getBody().size()); + assertThat(response).isNotNull(); + assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK); + assertThat(response.getBody()).isNotNull(); + assertThat(response.getBody().size()).isEqualTo(invoices.size()); } @@ -83,6 +82,7 @@ public class FeignContentEncodingTests { @SpringBootApplication(scanBasePackages = "org.springframework.cloud.openfeign.encoding.app") @Import(NoSecurityConfiguration.class) public static class Application { + } @Configuration @@ -98,5 +98,7 @@ public class FeignContentEncodingTests { Collections.singletonList(new Server("localhost", this.port))); return balancer; } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/Invoices.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/Invoices.java index 82b5f335..8ea306cd 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/Invoices.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/Invoices.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,13 +16,13 @@ package org.springframework.cloud.openfeign.encoding; -import org.springframework.cloud.openfeign.encoding.app.domain.Invoice; - import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; import java.util.Locale; +import org.springframework.cloud.openfeign.encoding.app.domain.Invoice; + /** * Utility class used for testing. * @@ -35,9 +35,11 @@ final class Invoices { for (int ind = 0; ind < count; ind++) { final Invoice invoice = new Invoice(); invoice.setTitle("Invoice " + (ind + 1)); - invoice.setAmount(new BigDecimal(String.format(Locale.US, "%.2f", Math.random() * 1000))); + invoice.setAmount(new BigDecimal( + String.format(Locale.US, "%.2f", Math.random() * 1000))); invoices.add(invoice); } return invoices; } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/client/InvoiceClient.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/client/InvoiceClient.java index 466d2669..34229888 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/client/InvoiceClient.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/client/InvoiceClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -38,4 +38,5 @@ public interface InvoiceClient { @RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity> saveInvoices(List invoices); + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/domain/Invoice.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/domain/Invoice.java index dc47351c..14fdcf55 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/domain/Invoice.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/domain/Invoice.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,7 +30,7 @@ public class Invoice { private BigDecimal amount; public String getTitle() { - return title; + return this.title; } public void setTitle(String title) { @@ -38,10 +38,11 @@ public class Invoice { } public BigDecimal getAmount() { - return amount; + return this.amount; } public void setAmount(BigDecimal amount) { this.amount = amount; } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/resource/InvoiceResource.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/resource/InvoiceResource.java index d6030d9b..4ccaad73 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/resource/InvoiceResource.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/app/resource/InvoiceResource.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,6 +16,11 @@ package org.springframework.cloud.openfeign.encoding.app.resource; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; + import org.springframework.cloud.openfeign.encoding.app.domain.Invoice; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -24,11 +29,6 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import java.math.BigDecimal; -import java.util.ArrayList; -import java.util.List; -import java.util.Locale; - /** * An sample REST controller, that potentially returns large response - used for testing. * @@ -43,8 +43,7 @@ public class InvoiceResource { return ResponseEntity.ok(createInvoiceList(100)); } - @RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, - produces = MediaType.APPLICATION_JSON_VALUE) + @RequestMapping(value = "invoices", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity> saveInvoices(@RequestBody List invoices) { return ResponseEntity.ok(invoices); @@ -55,9 +54,11 @@ public class InvoiceResource { for (int ind = 0; ind < count; ind++) { final Invoice invoice = new Invoice(); invoice.setTitle("Invoice " + (ind + 1)); - invoice.setAmount(new BigDecimal(String.format(Locale.US, "%.2f", Math.random() * 1000))); + invoice.setAmount(new BigDecimal( + String.format(Locale.US, "%.2f", Math.random() * 1000))); invoices.add(invoice); } return invoices; } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufNotInClasspathTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufNotInClasspathTest.java index 18930ea4..da2d5cb7 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufNotInClasspathTest.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufNotInClasspathTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,17 +39,17 @@ import static feign.Request.HttpMethod.POST; @ClassPathExclusions("protobuf-*.jar") public class ProtobufNotInClasspathTest { - @Test - public void testEncodeWhenProtobufNotInClasspath() { - ObjectFactory converters = new ObjectFactory() { - @Override - public HttpMessageConverters getObject() throws BeansException { - return new HttpMessageConverters(new StringHttpMessageConverter()); - } - }; - RequestTemplate requestTemplate = new RequestTemplate(); - requestTemplate.method(POST); - new SpringEncoder(converters).encode("a=b", String.class, requestTemplate); - } + @Test + public void testEncodeWhenProtobufNotInClasspath() { + ObjectFactory converters = new ObjectFactory() { + @Override + public HttpMessageConverters getObject() throws BeansException { + return new HttpMessageConverters(new StringHttpMessageConverter()); + } + }; + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.method(POST); + new SpringEncoder(converters).encode("a=b", String.class, requestTemplate); + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java index 2a9ca95a..9e54acb4 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufSpringEncoderTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -35,7 +35,6 @@ import org.apache.http.client.methods.HttpEntityEnclosingRequestBase; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.message.BasicHttpResponse; import org.apache.http.message.BasicStatusLine; -import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentMatchers; @@ -53,6 +52,8 @@ import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter; import static feign.Request.Body.encoded; import static feign.Request.HttpMethod.POST; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; /** * Test {@link SpringEncoder} with {@link ProtobufHttpMessageConverter} @@ -62,87 +63,92 @@ import static feign.Request.HttpMethod.POST; @RunWith(MockitoJUnitRunner.class) public class ProtobufSpringEncoderTest { - @Mock - private HttpClient httpClient; + @Mock + private HttpClient httpClient; - // a protobuf object with some content - private org.springframework.cloud.openfeign.encoding.proto.Request request = org.springframework.cloud.openfeign.encoding.proto.Request.newBuilder() - .setId(1000000) - .setMsg("Erlang/OTP 最初是爱立信为开发电信设备系统设计的编程语言平台," + - "电信设备(路由器、接入网关、…)典型设计是通过背板连接主控板卡与多块业务板卡的分布式系统。") - .build(); + // a protobuf object with some content + private org.springframework.cloud.openfeign.encoding.proto.Request request = org.springframework.cloud.openfeign.encoding.proto.Request + .newBuilder().setId(1000000).setMsg("Erlang/OTP 最初是爱立信为开发电信设备系统设计的编程语言平台," + + "电信设备(路由器、接入网关、…)典型设计是通过背板连接主控板卡与多块业务板卡的分布式系统。") + .build(); - @Test - public void testProtobuf() throws IOException, URISyntaxException { - // protobuf convert to request by feign and ProtobufHttpMessageConverter - RequestTemplate requestTemplate = newRequestTemplate(); - newEncoder().encode(request, Request.class, requestTemplate); - HttpEntity entity = toApacheHttpEntity(requestTemplate); - byte[] bytes = read(entity.getContent(), (int) entity.getContentLength()); + @Test + public void testProtobuf() throws IOException, URISyntaxException { + // protobuf convert to request by feign and ProtobufHttpMessageConverter + RequestTemplate requestTemplate = newRequestTemplate(); + newEncoder().encode(this.request, Request.class, requestTemplate); + HttpEntity entity = toApacheHttpEntity(requestTemplate); + byte[] bytes = read(entity.getContent(), (int) entity.getContentLength()); - Assert.assertArrayEquals(bytes, request.toByteArray()); - org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request.parseFrom(bytes); - Assert.assertEquals(request, copy); - } + assertThat(this.request.toByteArray()).isEqualTo(bytes); + org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request + .parseFrom(bytes); + assertThat(copy).isEqualTo(this.request); + } - @Test - public void testProtobufWithCharsetWillFail() throws IOException, URISyntaxException { - // protobuf convert to request by feign and ProtobufHttpMessageConverter - RequestTemplate requestTemplate = newRequestTemplate(); - newEncoder().encode(request, Request.class, requestTemplate); - // set a charset - requestTemplate - .body(encoded(requestTemplate.requestBody() - .asBytes(), StandardCharsets.UTF_8)); - HttpEntity entity = toApacheHttpEntity(requestTemplate); - byte[] bytes = read(entity.getContent(), (int) entity.getContentLength()); + @Test + public void testProtobufWithCharsetWillFail() throws IOException, URISyntaxException { + // protobuf convert to request by feign and ProtobufHttpMessageConverter + RequestTemplate requestTemplate = newRequestTemplate(); + newEncoder().encode(this.request, Request.class, requestTemplate); + // set a charset + requestTemplate.body( + encoded(requestTemplate.requestBody().asBytes(), StandardCharsets.UTF_8)); + HttpEntity entity = toApacheHttpEntity(requestTemplate); + byte[] bytes = read(entity.getContent(), (int) entity.getContentLength()); - // http request-body is different with original protobuf body - Assert.assertNotEquals(bytes.length, request.toByteArray().length); - try { - org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request.parseFrom(bytes); - Assert.fail("Expected an InvalidProtocolBufferException to be thrown"); - } catch (InvalidProtocolBufferException e) { - // success - } - } + // http request-body is different with original protobuf body + assertThat(this.request.toByteArray().length).isNotEqualTo(bytes.length); + try { + org.springframework.cloud.openfeign.encoding.proto.Request copy = org.springframework.cloud.openfeign.encoding.proto.Request + .parseFrom(bytes); + fail("Expected an InvalidProtocolBufferException to be thrown"); + } + catch (InvalidProtocolBufferException e) { + // success + } + } - private SpringEncoder newEncoder() { - ObjectFactory converters = new ObjectFactory() { - @Override - public HttpMessageConverters getObject() throws BeansException { - return new HttpMessageConverters(new ProtobufHttpMessageConverter()); - } - }; - return new SpringEncoder(converters); - } + private SpringEncoder newEncoder() { + ObjectFactory converters = new ObjectFactory() { + @Override + public HttpMessageConverters getObject() throws BeansException { + return new HttpMessageConverters(new ProtobufHttpMessageConverter()); + } + }; + return new SpringEncoder(converters); + } - private RequestTemplate newRequestTemplate() { - RequestTemplate requestTemplate = new RequestTemplate(); - requestTemplate.method(POST); - return requestTemplate; - } + private RequestTemplate newRequestTemplate() { + RequestTemplate requestTemplate = new RequestTemplate(); + requestTemplate.method(POST); + return requestTemplate; + } - private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate) throws IOException, URISyntaxException { - final List request = new ArrayList<>(1); - BDDMockito.given(httpClient.execute(ArgumentMatchers.any())) - .will(new Answer() { - @Override - public HttpResponse answer(InvocationOnMock invocationOnMock) throws Throwable { - request.add((HttpUriRequest) invocationOnMock.getArguments()[0]); - return new BasicHttpResponse(new BasicStatusLine(new ProtocolVersion("http", 1, 1), 200, null)); - } - }); - new ApacheHttpClient(httpClient).execute(requestTemplate.resolve(new HashMap<>()) - .request(), new feign.Request.Options()); - HttpUriRequest httpUriRequest = request.get(0); - return ((HttpEntityEnclosingRequestBase)httpUriRequest).getEntity(); - } + private HttpEntity toApacheHttpEntity(RequestTemplate requestTemplate) + throws IOException, URISyntaxException { + final List request = new ArrayList<>(1); + BDDMockito.given(this.httpClient.execute(ArgumentMatchers.any())) + .will(new Answer() { + @Override + public HttpResponse answer(InvocationOnMock invocationOnMock) + throws Throwable { + request.add((HttpUriRequest) invocationOnMock.getArguments()[0]); + return new BasicHttpResponse(new BasicStatusLine( + new ProtocolVersion("http", 1, 1), 200, null)); + } + }); + new ApacheHttpClient(this.httpClient).execute( + requestTemplate.resolve(new HashMap<>()).request(), + new feign.Request.Options()); + HttpUriRequest httpUriRequest = request.get(0); + return ((HttpEntityEnclosingRequestBase) httpUriRequest).getEntity(); + } - private byte[] read(InputStream in, int length) throws IOException { - byte[] bytes = new byte[length]; - in.read(bytes); - return bytes; - } + private byte[] read(InputStream in, int length) throws IOException { + byte[] bytes = new byte[length]; + in.read(bytes); + return bytes; + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufTest.java index f86cae2f..0df89df8 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufTest.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/ProtobufTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,53 +20,48 @@ package org.springframework.cloud.openfeign.encoding.proto; public final class ProtobufTest { - private ProtobufTest() {} - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistryLite registry) { - } - public static void registerAllExtensions( - com.google.protobuf.ExtensionRegistry registry) { - registerAllExtensions( - (com.google.protobuf.ExtensionRegistryLite) registry); - } - static final com.google.protobuf.Descriptors.Descriptor - internal_static_Request_descriptor; - static final - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable - internal_static_Request_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor internal_static_Request_descriptor; + static final com.google.protobuf.GeneratedMessageV3.FieldAccessorTable internal_static_Request_fieldAccessorTable; - public static com.google.protobuf.Descriptors.FileDescriptor - getDescriptor() { - return descriptor; - } - private static com.google.protobuf.Descriptors.FileDescriptor - descriptor; - static { - String[] descriptorData = { - "\n\023protobuf_test.proto\"\"\n\007Request\022\n\n\002id\030\001" + - " \001(\005\022\013\n\003msg\030\002 \001(\tB\024\n\020feign.httpclientP\001b" + - "\006proto3" - }; - com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = - new com.google.protobuf.Descriptors.FileDescriptor. InternalDescriptorAssigner() { - public com.google.protobuf.ExtensionRegistry assignDescriptors( - com.google.protobuf.Descriptors.FileDescriptor root) { - descriptor = root; - return null; - } - }; - com.google.protobuf.Descriptors.FileDescriptor - .internalBuildGeneratedFileFrom(descriptorData, - new com.google.protobuf.Descriptors.FileDescriptor[] { - }, assigner); - internal_static_Request_descriptor = - getDescriptor().getMessageTypes().get(0); - internal_static_Request_fieldAccessorTable = new - com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( - internal_static_Request_descriptor, - new String[] { "Id", "Msg", }); - } + private static com.google.protobuf.Descriptors.FileDescriptor descriptor; + + static { + String[] descriptorData = { + "\n\023protobuf_test.proto\"\"\n\007Request\022\n\n\002id\030\001" + + " \001(\005\022\013\n\003msg\030\002 \001(\tB\024\n\020feign.httpclientP\001b" + + "\006proto3" }; + com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner = new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() { + public com.google.protobuf.ExtensionRegistry assignDescriptors( + com.google.protobuf.Descriptors.FileDescriptor root) { + descriptor = root; + return null; + } + }; + com.google.protobuf.Descriptors.FileDescriptor.internalBuildGeneratedFileFrom( + descriptorData, new com.google.protobuf.Descriptors.FileDescriptor[] {}, + assigner); + internal_static_Request_descriptor = getDescriptor().getMessageTypes().get(0); + internal_static_Request_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_Request_descriptor, new String[] { "Id", "Msg", }); + } + + private ProtobufTest() { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions((com.google.protobuf.ExtensionRegistryLite) registry); + } + + public static com.google.protobuf.Descriptors.FileDescriptor getDescriptor() { + return descriptor; + } + + // @@protoc_insertion_point(outer_class_scope) - // @@protoc_insertion_point(outer_class_scope) } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/Request.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/Request.java index 83905112..17f2ca4d 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/Request.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/Request.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,566 +22,583 @@ package org.springframework.cloud.openfeign.encoding.proto; /** * Protobuf type {@code Request} */ -public final class Request extends - com.google.protobuf.GeneratedMessageV3 implements - // @@protoc_insertion_point(message_implements:Request) - org.springframework.cloud.openfeign.encoding.proto.RequestOrBuilder { -private static final long serialVersionUID = 0L; - // Use Request.newBuilder() to construct. - private Request(com.google.protobuf.GeneratedMessageV3.Builder builder) { - super(builder); - } - private Request() { - id_ = 0; - msg_ = ""; - } +public final class Request extends com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:Request) + org.springframework.cloud.openfeign.encoding.proto.RequestOrBuilder { - @Override - public final com.google.protobuf.UnknownFieldSet - getUnknownFields() { - return this.unknownFields; - } - private Request( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - this(); - int mutable_bitField0_ = 0; - com.google.protobuf.UnknownFieldSet.Builder unknownFields = - com.google.protobuf.UnknownFieldSet.newBuilder(); - try { - boolean done = false; - while (!done) { - int tag = input.readTag(); - switch (tag) { - case 0: - done = true; - break; - default: { - if (!parseUnknownFieldProto3( - input, unknownFields, extensionRegistry, tag)) { - done = true; - } - break; - } - case 8: { + public static final int ID_FIELD_NUMBER = 1; - id_ = input.readInt32(); - break; - } - case 18: { - String s = input.readStringRequireUtf8(); + public static final int MSG_FIELD_NUMBER = 2; - msg_ = s; - break; - } - } - } - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - throw e.setUnfinishedMessage(this); - } catch (java.io.IOException e) { - throw new com.google.protobuf.InvalidProtocolBufferException( - e).setUnfinishedMessage(this); - } finally { - this.unknownFields = unknownFields.build(); - makeExtensionsImmutable(); - } - } - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_descriptor; - } + private static final long serialVersionUID = 0L; - protected FieldAccessorTable - internalGetFieldAccessorTable() { - return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_fieldAccessorTable - .ensureFieldAccessorsInitialized( - Request.class, Request.Builder.class); - } + // @@protoc_insertion_point(class_scope:Request) + private static final Request DEFAULT_INSTANCE; - public static final int ID_FIELD_NUMBER = 1; - private int id_; - /** - * int32 id = 1; - */ - public int getId() { - return id_; - } + private static final com.google.protobuf.Parser PARSER = new com.google.protobuf.AbstractParser() { + public Request parsePartialFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new Request(input, extensionRegistry); + } + }; - public static final int MSG_FIELD_NUMBER = 2; - private volatile Object msg_; - /** - * string msg = 2; - */ - public String getMsg() { - Object ref = msg_; - if (ref instanceof String) { - return (String) ref; - } else { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - msg_ = s; - return s; - } - } - /** - * string msg = 2; - */ - public com.google.protobuf.ByteString - getMsgBytes() { - Object ref = msg_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (String) ref); - msg_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } + static { + DEFAULT_INSTANCE = new Request(); + } - private byte memoizedIsInitialized = -1; - public final boolean isInitialized() { - byte isInitialized = memoizedIsInitialized; - if (isInitialized == 1) return true; - if (isInitialized == 0) return false; + private int id_; - memoizedIsInitialized = 1; - return true; - } + private volatile Object msg_; - public void writeTo(com.google.protobuf.CodedOutputStream output) - throws java.io.IOException { - if (id_ != 0) { - output.writeInt32(1, id_); - } - if (!getMsgBytes().isEmpty()) { - com.google.protobuf.GeneratedMessageV3.writeString(output, 2, msg_); - } - unknownFields.writeTo(output); - } + private byte memoizedIsInitialized = -1; - public int getSerializedSize() { - int size = memoizedSize; - if (size != -1) return size; + // Use Request.newBuilder() to construct. + private Request(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } - size = 0; - if (id_ != 0) { - size += com.google.protobuf.CodedOutputStream - .computeInt32Size(1, id_); - } - if (!getMsgBytes().isEmpty()) { - size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, msg_); - } - size += unknownFields.getSerializedSize(); - memoizedSize = size; - return size; - } + private Request() { + this.id_ = 0; + this.msg_ = ""; + } - @Override - public boolean equals(final Object obj) { - if (obj == this) { - return true; - } - if (!(obj instanceof Request)) { - return super.equals(obj); - } - Request other = (Request) obj; + private Request(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = com.google.protobuf.UnknownFieldSet + .newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + default: + if (!parseUnknownFieldProto3(input, unknownFields, extensionRegistry, + tag)) { + done = true; + } + break; + case 8: + this.id_ = input.readInt32(); + break; + case 18: + String s = input.readStringRequireUtf8(); + this.msg_ = s; + break; + } + } + } + catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } + catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException(e) + .setUnfinishedMessage(this); + } + finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } - boolean result = true; - result = result && (getId() - == other.getId()); - result = result && getMsg() - .equals(other.getMsg()); - result = result && unknownFields.equals(other.unknownFields); - return result; - } + public static com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_descriptor; + } - @Override - public int hashCode() { - if (memoizedHashCode != 0) { - return memoizedHashCode; - } - int hash = 41; - hash = (19 * hash) + getDescriptor().hashCode(); - hash = (37 * hash) + ID_FIELD_NUMBER; - hash = (53 * hash) + getId(); - hash = (37 * hash) + MSG_FIELD_NUMBER; - hash = (53 * hash) + getMsg().hashCode(); - hash = (29 * hash) + unknownFields.hashCode(); - memoizedHashCode = hash; - return hash; - } + public static Request parseFrom(java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } - public static Request parseFrom( - java.nio.ByteBuffer data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static Request parseFrom( - java.nio.ByteBuffer data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static Request parseFrom( - com.google.protobuf.ByteString data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static Request parseFrom( - com.google.protobuf.ByteString data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static Request parseFrom(byte[] data) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data); - } - public static Request parseFrom( - byte[] data, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return PARSER.parseFrom(data, extensionRegistry); - } - public static Request parseFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static Request parseFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } - public static Request parseDelimitedFrom(java.io.InputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input); - } - public static Request parseDelimitedFrom( - java.io.InputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseDelimitedWithIOException(PARSER, input, extensionRegistry); - } - public static Request parseFrom( - com.google.protobuf.CodedInputStream input) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input); - } - public static Request parseFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - return com.google.protobuf.GeneratedMessageV3 - .parseWithIOException(PARSER, input, extensionRegistry); - } + public static Request parseFrom(java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } - public Builder newBuilderForType() { return newBuilder(); } - public static Builder newBuilder() { - return DEFAULT_INSTANCE.toBuilder(); - } - public static Builder newBuilder(Request prototype) { - return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); - } - public Builder toBuilder() { - return this == DEFAULT_INSTANCE - ? new Builder() : new Builder().mergeFrom(this); - } + public static Request parseFrom(com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } - @Override - protected Builder newBuilderForType( - BuilderParent parent) { - Builder builder = new Builder(parent); - return builder; - } - /** - * Protobuf type {@code Request} - */ - public static final class Builder extends - com.google.protobuf.GeneratedMessageV3.Builder implements - // @@protoc_insertion_point(builder_implements:Request) - org.springframework.cloud.openfeign.encoding.proto.RequestOrBuilder { - public static final com.google.protobuf.Descriptors.Descriptor - getDescriptor() { - return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_descriptor; - } + public static Request parseFrom(com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } - protected FieldAccessorTable - internalGetFieldAccessorTable() { - return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_fieldAccessorTable - .ensureFieldAccessorsInitialized( - Request.class, Request.Builder.class); - } + public static Request parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } - // Construct using org.springframework.cloud.openfeign.encoding.proto.Request.newBuilder() - private Builder() { - maybeForceBuilderInitialization(); - } + public static Request parseFrom(byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } - private Builder( - BuilderParent parent) { - super(parent); - maybeForceBuilderInitialization(); - } - private void maybeForceBuilderInitialization() { - if (com.google.protobuf.GeneratedMessageV3 - .alwaysUseFieldBuilders) { - } - } - public Builder clear() { - super.clear(); - id_ = 0; + public static Request parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } - msg_ = ""; + public static Request parseFrom(java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, + extensionRegistry); + } - return this; - } + public static Request parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } - public com.google.protobuf.Descriptors.Descriptor - getDescriptorForType() { - return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_descriptor; - } + public static Request parseDelimitedFrom(java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } - public Request getDefaultInstanceForType() { - return Request.getDefaultInstance(); - } + public static Request parseFrom(com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input); + } - public Request build() { - Request result = buildPartial(); - if (!result.isInitialized()) { - throw newUninitializedMessageException(result); - } - return result; - } + public static Request parseFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3.parseWithIOException(PARSER, input, + extensionRegistry); + } - public Request buildPartial() { - Request result = new Request(this); - result.id_ = id_; - result.msg_ = msg_; - onBuilt(); - return result; - } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } - public Builder clone() { - return (Builder) super.clone(); - } - public Builder setField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.setField(field, value); - } - public Builder clearField( - com.google.protobuf.Descriptors.FieldDescriptor field) { - return (Builder) super.clearField(field); - } - public Builder clearOneof( - com.google.protobuf.Descriptors.OneofDescriptor oneof) { - return (Builder) super.clearOneof(oneof); - } - public Builder setRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - int index, Object value) { - return (Builder) super.setRepeatedField(field, index, value); - } - public Builder addRepeatedField( - com.google.protobuf.Descriptors.FieldDescriptor field, - Object value) { - return (Builder) super.addRepeatedField(field, value); - } - public Builder mergeFrom(com.google.protobuf.Message other) { - if (other instanceof Request) { - return mergeFrom((Request)other); - } else { - super.mergeFrom(other); - return this; - } - } + public static Builder newBuilder(Request prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } - public Builder mergeFrom(Request other) { - if (other == Request.getDefaultInstance()) return this; - if (other.getId() != 0) { - setId(other.getId()); - } - if (!other.getMsg().isEmpty()) { - msg_ = other.msg_; - onChanged(); - } - this.mergeUnknownFields(other.unknownFields); - onChanged(); - return this; - } + public static Request getDefaultInstance() { + return DEFAULT_INSTANCE; + } - public final boolean isInitialized() { - return true; - } + public static com.google.protobuf.Parser parser() { + return PARSER; + } - public Builder mergeFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws java.io.IOException { - Request parsedMessage = null; - try { - parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); - } catch (com.google.protobuf.InvalidProtocolBufferException e) { - parsedMessage = (Request) e.getUnfinishedMessage(); - throw e.unwrapIOException(); - } finally { - if (parsedMessage != null) { - mergeFrom(parsedMessage); - } - } - return this; - } + @Override + public com.google.protobuf.UnknownFieldSet getUnknownFields() { + return this.unknownFields; + } - private int id_ ; - /** - * int32 id = 1; - */ - public int getId() { - return id_; - } - /** - * int32 id = 1; - */ - public Builder setId(int value) { + protected FieldAccessorTable internalGetFieldAccessorTable() { + return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_fieldAccessorTable + .ensureFieldAccessorsInitialized(Request.class, Request.Builder.class); + } - id_ = value; - onChanged(); - return this; - } - /** - * int32 id = 1; - */ - public Builder clearId() { + /** + * int32 id = 1; + */ + public int getId() { + return this.id_; + } - id_ = 0; - onChanged(); - return this; - } + /** + * string msg = 2; + */ + public String getMsg() { + Object ref = this.msg_; + if (ref instanceof String) { + return (String) ref; + } + else { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + this.msg_ = s; + return s; + } + } - private Object msg_ = ""; - /** - * string msg = 2; - */ - public String getMsg() { - Object ref = msg_; - if (!(ref instanceof String)) { - com.google.protobuf.ByteString bs = - (com.google.protobuf.ByteString) ref; - String s = bs.toStringUtf8(); - msg_ = s; - return s; - } else { - return (String) ref; - } - } - /** - * string msg = 2; - */ - public com.google.protobuf.ByteString - getMsgBytes() { - Object ref = msg_; - if (ref instanceof String) { - com.google.protobuf.ByteString b = - com.google.protobuf.ByteString.copyFromUtf8( - (String) ref); - msg_ = b; - return b; - } else { - return (com.google.protobuf.ByteString) ref; - } - } - /** - * string msg = 2; - */ - public Builder setMsg( - String value) { - if (value == null) { - throw new NullPointerException(); - } + /** + * string msg = 2; + */ + public com.google.protobuf.ByteString getMsgBytes() { + Object ref = this.msg_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = com.google.protobuf.ByteString + .copyFromUtf8((String) ref); + this.msg_ = b; + return b; + } + else { + return (com.google.protobuf.ByteString) ref; + } + } - msg_ = value; - onChanged(); - return this; - } - /** - * string msg = 2; - */ - public Builder clearMsg() { + public boolean isInitialized() { + byte isInitialized = this.memoizedIsInitialized; + if (isInitialized == 1) { + return true; + } + if (isInitialized == 0) { + return false; + } + this.memoizedIsInitialized = 1; + return true; + } - msg_ = getDefaultInstance().getMsg(); - onChanged(); - return this; - } - /** - * string msg = 2; - */ - public Builder setMsgBytes( - com.google.protobuf.ByteString value) { - if (value == null) { - throw new NullPointerException(); - } - checkByteStringIsUtf8(value); + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (this.id_ != 0) { + output.writeInt32(1, this.id_); + } + if (!getMsgBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, this.msg_); + } + this.unknownFields.writeTo(output); + } - msg_ = value; - onChanged(); - return this; - } - public final Builder setUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.setUnknownFieldsProto3(unknownFields); - } + public int getSerializedSize() { + int size = this.memoizedSize; + if (size != -1) { + return size; + } - public final Builder mergeUnknownFields( - final com.google.protobuf.UnknownFieldSet unknownFields) { - return super.mergeUnknownFields(unknownFields); - } + size = 0; + if (this.id_ != 0) { + size += com.google.protobuf.CodedOutputStream.computeInt32Size(1, this.id_); + } + if (!getMsgBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, + this.msg_); + } + size += this.unknownFields.getSerializedSize(); + this.memoizedSize = size; + return size; + } + @Override + public boolean equals(final Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof Request)) { + return super.equals(obj); + } + Request other = (Request) obj; - // @@protoc_insertion_point(builder_scope:Request) - } + boolean result = true; + result = result && (getId() == other.getId()); + result = result && getMsg().equals(other.getMsg()); + result = result && this.unknownFields.equals(other.unknownFields); + return result; + } - // @@protoc_insertion_point(class_scope:Request) - private static final Request DEFAULT_INSTANCE; - static { - DEFAULT_INSTANCE = new Request(); - } + @Override + public int hashCode() { + if (this.memoizedHashCode != 0) { + return this.memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + ID_FIELD_NUMBER; + hash = (53 * hash) + getId(); + hash = (37 * hash) + MSG_FIELD_NUMBER; + hash = (53 * hash) + getMsg().hashCode(); + hash = (29 * hash) + this.unknownFields.hashCode(); + this.memoizedHashCode = hash; + return hash; + } - public static Request getDefaultInstance() { - return DEFAULT_INSTANCE; - } + public Builder newBuilderForType() { + return newBuilder(); + } - private static final com.google.protobuf.Parser - PARSER = new com.google.protobuf.AbstractParser() { - public Request parsePartialFrom( - com.google.protobuf.CodedInputStream input, - com.google.protobuf.ExtensionRegistryLite extensionRegistry) - throws com.google.protobuf.InvalidProtocolBufferException { - return new Request(input, extensionRegistry); - } - }; + public Builder toBuilder() { + return this == DEFAULT_INSTANCE ? new Builder() : new Builder().mergeFrom(this); + } - public static com.google.protobuf.Parser parser() { - return PARSER; - } + @Override + protected Builder newBuilderForType(BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } - @Override - public com.google.protobuf.Parser getParserForType() { - return PARSER; - } + @Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } - public Request getDefaultInstanceForType() { - return DEFAULT_INSTANCE; - } + public Request getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + + /** + * Protobuf type {@code Request} + */ + public static final class Builder + extends com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:Request) + org.springframework.cloud.openfeign.encoding.proto.RequestOrBuilder { + + private int id_; + + private Object msg_ = ""; + + // Construct using + // org.springframework.cloud.openfeign.encoding.proto.Request.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder(BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + + public static com.google.protobuf.Descriptors.Descriptor getDescriptor() { + return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_descriptor; + } + + protected FieldAccessorTable internalGetFieldAccessorTable() { + return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_fieldAccessorTable + .ensureFieldAccessorsInitialized(Request.class, + Request.Builder.class); + } + + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders) { + // TODO: Remove this? + } + } + + public Builder clear() { + super.clear(); + this.id_ = 0; + this.msg_ = ""; + return this; + } + + public com.google.protobuf.Descriptors.Descriptor getDescriptorForType() { + return org.springframework.cloud.openfeign.encoding.proto.ProtobufTest.internal_static_Request_descriptor; + } + + public Request getDefaultInstanceForType() { + return Request.getDefaultInstance(); + } + + public Request build() { + Request result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + public Request buildPartial() { + Request result = new Request(this); + result.id_ = this.id_; + result.msg_ = this.msg_; + onBuilt(); + return result; + } + + public Builder clone() { + return (Builder) super.clone(); + } + + public Builder setField(com.google.protobuf.Descriptors.FieldDescriptor field, + Object value) { + return (Builder) super.setField(field, value); + } + + public Builder clearField(com.google.protobuf.Descriptors.FieldDescriptor field) { + return (Builder) super.clearField(field); + } + + public Builder clearOneof(com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return (Builder) super.clearOneof(oneof); + } + + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, int index, + Object value) { + return (Builder) super.setRepeatedField(field, index, value); + } + + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, Object value) { + return (Builder) super.addRepeatedField(field, value); + } + + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof Request) { + return mergeFrom((Request) other); + } + else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(Request other) { + if (other == Request.getDefaultInstance()) { + return this; + } + if (other.getId() != 0) { + setId(other.getId()); + } + if (!other.getMsg().isEmpty()) { + this.msg_ = other.msg_; + onChanged(); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + public boolean isInitialized() { + return true; + } + + public Builder mergeFrom(com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + Request parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } + catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (Request) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } + finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + /** + * int32 id = 1; + */ + public int getId() { + return this.id_; + } + + /** + * int32 id = 1; + */ + public Builder setId(int value) { + + this.id_ = value; + onChanged(); + return this; + } + + /** + * int32 id = 1; + */ + public Builder clearId() { + + this.id_ = 0; + onChanged(); + return this; + } + + /** + * string msg = 2; + */ + public String getMsg() { + Object ref = this.msg_; + if (!(ref instanceof String)) { + com.google.protobuf.ByteString bs = (com.google.protobuf.ByteString) ref; + String s = bs.toStringUtf8(); + this.msg_ = s; + return s; + } + else { + return (String) ref; + } + } + + /** + * string msg = 2; + */ + public Builder setMsg(String value) { + if (value == null) { + throw new NullPointerException(); + } + + this.msg_ = value; + onChanged(); + return this; + } + + /** + * string msg = 2; + */ + public com.google.protobuf.ByteString getMsgBytes() { + Object ref = this.msg_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = com.google.protobuf.ByteString + .copyFromUtf8((String) ref); + this.msg_ = b; + return b; + } + else { + return (com.google.protobuf.ByteString) ref; + } + } + + /** + * string msg = 2; + */ + public Builder setMsgBytes(com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + this.msg_ = value; + onChanged(); + return this; + } + + /** + * string msg = 2; + */ + public Builder clearMsg() { + + this.msg_ = getDefaultInstance().getMsg(); + onChanged(); + return this; + } + + public Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFieldsProto3(unknownFields); + } + + public Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + // @@protoc_insertion_point(builder_scope:Request) + + } } - diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/RequestOrBuilder.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/RequestOrBuilder.java index 59a91af2..6faab1ee 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/RequestOrBuilder.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/encoding/proto/RequestOrBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,21 +20,22 @@ package org.springframework.cloud.openfeign.encoding.proto; public interface RequestOrBuilder extends - // @@protoc_insertion_point(interface_extends:Request) - com.google.protobuf.MessageOrBuilder { + // @@protoc_insertion_point(interface_extends:Request) + com.google.protobuf.MessageOrBuilder { - /** - * int32 id = 1; - */ - int getId(); + /** + * int32 id = 1; + */ + int getId(); + + /** + * string msg = 2; + */ + String getMsg(); + + /** + * string msg = 2; + */ + com.google.protobuf.ByteString getMsgBytes(); - /** - * string msg = 2; - */ - String getMsg(); - /** - * string msg = 2; - */ - com.google.protobuf.ByteString - getMsgBytes(); } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityApplication.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityApplication.java index ee01932d..21a40e7f 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityApplication.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityApplication.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityTests.java index e3f8fd5f..d605c535 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/HystrixSecurityTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,50 +51,27 @@ import static org.assertj.core.api.Assertions.assertThat; /** * Tests that a secured web service returning values using a feign client properly access * the security context from a hystrix command. + * * @author Daniel Lavoie */ @RunWith(SpringRunner.class) @DirtiesContext -@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, - properties = { "feign.hystrix.enabled=true"}) +@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, properties = { + "feign.hystrix.enabled=true" }) @ActiveProfiles("proxysecurity") public class HystrixSecurityTests { + @Autowired private CustomConcurrenyStrategy customConcurrenyStrategy; @LocalServerPort private String serverPort; - //TODO: move to constants in TestAutoConfiguration + // TODO: move to constants in TestAutoConfiguration private String username = "user"; private String password = "password"; - @Test - public void testSecurityConcurrencyStrategyInstalled() { - HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); - assertThat(concurrencyStrategy).isInstanceOf(SecurityContextConcurrencyStrategy.class); - } - - @Test - public void testFeignHystrixSecurity() { - HttpHeaders headers = createBasicAuthHeader(username, password); - - ResponseEntity entity = new RestTemplate() - .exchange("http://localhost:" + serverPort + "/proxy-username", - HttpMethod.GET, new HttpEntity(headers), String.class); - - assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); - - assertThat(entity.getBody()) - .as("Username should have been intercepted by feign interceptor.") - .isEqualTo(username); - - assertThat(customConcurrenyStrategy.isHookCalled()) - .as("Custom hook should have been called.") - .isTrue(); - } - public static HttpHeaders createBasicAuthHeader(final String username, final String password) { return new HttpHeaders() { @@ -109,10 +86,38 @@ public class HystrixSecurityTests { }; } + @Test + public void testSecurityConcurrencyStrategyInstalled() { + HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance() + .getConcurrencyStrategy(); + assertThat(concurrencyStrategy) + .isInstanceOf(SecurityContextConcurrencyStrategy.class); + } + + @Test + public void testFeignHystrixSecurity() { + HttpHeaders headers = createBasicAuthHeader(this.username, this.password); + + ResponseEntity entity = new RestTemplate().exchange( + "http://localhost:" + this.serverPort + "/proxy-username", HttpMethod.GET, + new HttpEntity(headers), String.class); + + assertThat(entity.getStatusCode()).isEqualTo(HttpStatus.OK); + + assertThat(entity.getBody()) + .as("Username should have been intercepted by feign interceptor.") + .isEqualTo(this.username); + + assertThat(this.customConcurrenyStrategy.isHookCalled()) + .as("Custom hook should have been called.").isTrue(); + } + @SpringBootConfiguration @Import(HystrixSecurityApplication.class) @RibbonClient(name = "username", configuration = LocalRibbonClientConfiguration.class) - protected static class TestConfig { } + protected static class TestConfig { + + } protected static class LocalRibbonClientConfiguration { @@ -125,4 +130,5 @@ public class HystrixSecurityTests { } } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/CustomConcurrenyStrategy.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/CustomConcurrenyStrategy.java index 6bcc7736..de719c16 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/CustomConcurrenyStrategy.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/CustomConcurrenyStrategy.java @@ -1,10 +1,27 @@ -package org.springframework.cloud.openfeign.hystrix.security.app; +/* + * Copyright 2013-2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ -import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; +package org.springframework.cloud.openfeign.hystrix.security.app; import java.util.concurrent.Callable; +import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; + public class CustomConcurrenyStrategy extends HystrixConcurrencyStrategy { + private boolean hookCalled; @Override @@ -15,6 +32,7 @@ public class CustomConcurrenyStrategy extends HystrixConcurrencyStrategy { } public boolean isHookCalled() { - return hookCalled; + return this.hookCalled; } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/ProxyUsernameController.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/ProxyUsernameController.java index bf4ccd5b..afd629e8 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/ProxyUsernameController.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/ProxyUsernameController.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,13 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/proxy-username") public class ProxyUsernameController { + @Autowired private UsernameClient usernameClient; @RequestMapping public String getUsername() { - return usernameClient.getUsername(); + return this.usernameClient.getUsername(); } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/TestInterceptor.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/TestInterceptor.java index 1a5dae00..abb61d15 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/TestInterceptor.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/TestInterceptor.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,15 +24,17 @@ import org.springframework.security.core.context.SecurityContextHolder; /** * This interceptor should be called from an Hyxtrix command execution thread. It is * access the SecurityContext and settings an http header from the authentication details. - * + * * @author Daniel Lavoie */ public class TestInterceptor implements RequestInterceptor { @Override public void apply(RequestTemplate template) { - if (SecurityContextHolder.getContext().getAuthentication() != null) + if (SecurityContextHolder.getContext().getAuthentication() != null) { template.header("username", SecurityContextHolder.getContext().getAuthentication().getName()); + } } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameClient.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameClient.java index 056651a3..cec9c925 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameClient.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameClient.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,4 +27,5 @@ public interface UsernameClient { @RequestMapping("/username") String getUsername(); + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameController.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameController.java index daacc0d5..66c75f1f 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameController.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/hystrix/security/app/UsernameController.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,8 +26,10 @@ import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/username") public class UsernameController { + @RequestMapping - public String getUsername(@RequestHeader String username){ + public String getUsername(@RequestHeader String username) { return username; } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/invalid/FeignClientValidationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/invalid/FeignClientValidationTests.java index a67d80e1..3077bb52 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/invalid/FeignClientValidationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/invalid/FeignClientValidationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,10 +19,10 @@ package org.springframework.cloud.openfeign.invalid; import feign.Feign; import feign.hystrix.FallbackFactory; import feign.hystrix.HystrixFeign; - import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; + import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; @@ -37,7 +37,7 @@ import org.springframework.context.annotation.Import; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Dave Syer @@ -53,6 +53,81 @@ public class FeignClientValidationTests { new AnnotationConfigApplicationContext(NameAndValueConfiguration.class); } + @Test + public void testServiceIdAndValue() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + LoadBalancerAutoConfiguration.class, RibbonAutoConfiguration.class, + FeignRibbonClientAutoConfiguration.class, + NameAndServiceIdConfiguration.class); + assertThat(context.getBean(NameAndServiceIdConfiguration.Client.class)) + .isNotNull(); + context.close(); + } + + @Test + public void testDuplicatedClientNames() { + AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); + context.setAllowBeanDefinitionOverriding(false); + context.register(LoadBalancerAutoConfiguration.class, + RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class, + DuplicatedFeignClientNamesConfiguration.class); + context.refresh(); + assertThat( + context.getBean(DuplicatedFeignClientNamesConfiguration.FooClient.class)) + .isNotNull(); + assertThat( + context.getBean(DuplicatedFeignClientNamesConfiguration.BarClient.class)) + .isNotNull(); + context.close(); + } + + @Test + public void testNotLegalHostname() { + this.expected.expectMessage("not legal hostname (foo_bar)"); + new AnnotationConfigApplicationContext(BadHostnameConfiguration.class); + } + + @Test + public void testMissingFallback() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + MissingFallbackConfiguration.class)) { + this.expected.expectMessage("No fallback instance of type"); + assertThat(context.getBean(MissingFallbackConfiguration.Client.class)) + .isNotNull(); + } + } + + @Test + public void testWrongFallbackType() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + WrongFallbackTypeConfiguration.class)) { + this.expected.expectMessage("Incompatible fallback instance"); + assertThat(context.getBean(WrongFallbackTypeConfiguration.Client.class)) + .isNotNull(); + } + } + + @Test + public void testMissingFallbackFactory() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + MissingFallbackFactoryConfiguration.class)) { + this.expected.expectMessage("No fallbackFactory instance of type"); + assertThat(context.getBean(MissingFallbackFactoryConfiguration.Client.class)) + .isNotNull(); + } + } + + @Test + public void testWrongFallbackFactoryType() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( + WrongFallbackFactoryTypeConfiguration.class)) { + this.expected.expectMessage("Incompatible fallbackFactory instance"); + assertThat( + context.getBean(WrongFallbackFactoryTypeConfiguration.Client.class)) + .isNotNull(); + } + } + @Configuration @Import(FeignAutoConfiguration.class) @EnableFeignClients(clients = NameAndValueConfiguration.Client.class) @@ -60,75 +135,53 @@ public class FeignClientValidationTests { @FeignClient(value = "foo", name = "bar") interface Client { + @RequestMapping(method = RequestMethod.GET, value = "/") String get(); + } } - @Test - public void testServiceIdAndValue() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - LoadBalancerAutoConfiguration.class, - RibbonAutoConfiguration.class, - FeignRibbonClientAutoConfiguration.class, - NameAndServiceIdConfiguration.class); - assertNotNull(context.getBean(NameAndServiceIdConfiguration.Client.class)); - context.close(); - } - @Configuration - @Import({FeignAutoConfiguration.class, HttpClientConfiguration.class}) + @Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class }) @EnableFeignClients(clients = NameAndServiceIdConfiguration.Client.class) protected static class NameAndServiceIdConfiguration { @FeignClient(name = "bar", serviceId = "foo") interface Client { + @RequestMapping(method = RequestMethod.GET, value = "/") String get(); - } - } - @Test - public void testDuplicatedClientNames() { - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); - context.setAllowBeanDefinitionOverriding(false); - context.register( - LoadBalancerAutoConfiguration.class, - RibbonAutoConfiguration.class, - FeignRibbonClientAutoConfiguration.class, - DuplicatedFeignClientNamesConfiguration.class - ); - context.refresh(); - assertNotNull(context.getBean(DuplicatedFeignClientNamesConfiguration.FooClient.class)); - assertNotNull(context.getBean(DuplicatedFeignClientNamesConfiguration.BarClient.class)); - context.close(); + } + } @Configuration - @Import({FeignAutoConfiguration.class, HttpClientConfiguration.class}) - @EnableFeignClients(clients = {DuplicatedFeignClientNamesConfiguration.FooClient.class, - DuplicatedFeignClientNamesConfiguration.BarClient.class}) + @Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class }) + @EnableFeignClients(clients = { + DuplicatedFeignClientNamesConfiguration.FooClient.class, + DuplicatedFeignClientNamesConfiguration.BarClient.class }) protected static class DuplicatedFeignClientNamesConfiguration { @FeignClient(contextId = "foo", name = "bar") interface FooClient { + @RequestMapping(method = RequestMethod.GET, value = "/") String get(); + } @FeignClient(name = "bar") interface BarClient { + @RequestMapping(method = RequestMethod.GET, value = "/") String get(); - } - } - @Test - public void testNotLegalHostname() { - this.expected.expectMessage("not legal hostname (foo_bar)"); - new AnnotationConfigApplicationContext(BadHostnameConfiguration.class); + } + } @Configuration @@ -138,53 +191,41 @@ public class FeignClientValidationTests { @FeignClient("foo_bar") interface Client { + @RequestMapping(method = RequestMethod.GET, value = "/") String get(); + } } - @Test - public void testMissingFallback() { - try ( - AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - MissingFallbackConfiguration.class)) { - this.expected.expectMessage("No fallback instance of type"); - assertNotNull(context.getBean(MissingFallbackConfiguration.Client.class)); - } - } - @Configuration @Import(FeignAutoConfiguration.class) @EnableFeignClients(clients = MissingFallbackConfiguration.Client.class) protected static class MissingFallbackConfiguration { - @FeignClient(name = "foobar", url = "http://localhost", fallback = ClientFallback.class) - interface Client { - @RequestMapping(method = RequestMethod.GET, value = "/") - String get(); - } - - class ClientFallback implements Client { - @Override - public String get() { - return null; - } - } - @Bean public Feign.Builder feignBuilder() { return HystrixFeign.builder(); } - } - @Test - public void testWrongFallbackType() { - try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - WrongFallbackTypeConfiguration.class)) { - this.expected.expectMessage("Incompatible fallback instance"); - assertNotNull(context.getBean(WrongFallbackTypeConfiguration.Client.class)); + @FeignClient(name = "foobar", url = "http://localhost", fallback = ClientFallback.class) + interface Client { + + @RequestMapping(method = RequestMethod.GET, value = "/") + String get(); + } + + class ClientFallback implements Client { + + @Override + public String get() { + return null; + } + + } + } @Configuration @@ -192,34 +233,28 @@ public class FeignClientValidationTests { @EnableFeignClients(clients = WrongFallbackTypeConfiguration.Client.class) protected static class WrongFallbackTypeConfiguration { - @FeignClient(name = "foobar", url = "http://localhost", fallback = Dummy.class) - interface Client { - @RequestMapping(method = RequestMethod.GET, value = "/") - String get(); - } - @Bean Dummy dummy() { return new Dummy(); } - class Dummy { - } - @Bean public Feign.Builder feignBuilder() { return HystrixFeign.builder(); } - } + @FeignClient(name = "foobar", url = "http://localhost", fallback = Dummy.class) + interface Client { + + @RequestMapping(method = RequestMethod.GET, value = "/") + String get(); - @Test - public void testMissingFallbackFactory() { - try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - MissingFallbackFactoryConfiguration.class)) { - this.expected.expectMessage("No fallbackFactory instance of type"); - assertNotNull(context.getBean(MissingFallbackFactoryConfiguration.Client.class)); } + + class Dummy { + + } + } @Configuration @@ -227,10 +262,17 @@ public class FeignClientValidationTests { @EnableFeignClients(clients = MissingFallbackFactoryConfiguration.Client.class) protected static class MissingFallbackFactoryConfiguration { + @Bean + public Feign.Builder feignBuilder() { + return HystrixFeign.builder(); + } + @FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = ClientFallback.class) interface Client { + @RequestMapping(method = RequestMethod.GET, value = "/") String get(); + } class ClientFallback implements FallbackFactory { @@ -239,21 +281,9 @@ public class FeignClientValidationTests { public Client create(Throwable cause) { return null; } + } - @Bean - public Feign.Builder feignBuilder() { - return HystrixFeign.builder(); - } - } - - @Test - public void testWrongFallbackFactoryType() { - try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - WrongFallbackFactoryTypeConfiguration.class)) { - this.expected.expectMessage("Incompatible fallbackFactory instance"); - assertNotNull(context.getBean(WrongFallbackFactoryTypeConfiguration.Client.class)); - } } @Configuration @@ -261,24 +291,28 @@ public class FeignClientValidationTests { @EnableFeignClients(clients = WrongFallbackFactoryTypeConfiguration.Client.class) protected static class WrongFallbackFactoryTypeConfiguration { - @FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = Dummy.class) - interface Client { - @RequestMapping(method = RequestMethod.GET, value = "/") - String get(); - } - @Bean Dummy dummy() { return new Dummy(); } - class Dummy { - } - @Bean public Feign.Builder feignBuilder() { return HystrixFeign.builder(); } + @FeignClient(name = "foobar", url = "http://localhost", fallbackFactory = Dummy.class) + interface Client { + + @RequestMapping(method = RequestMethod.GET, value = "/") + String get(); + + } + + class Dummy { + + } + } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/CachingSpringLoadBalancerFactoryTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/CachingSpringLoadBalancerFactoryTests.java index ad9172ce..b4acdd29 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/CachingSpringLoadBalancerFactoryTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/CachingSpringLoadBalancerFactoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,10 +23,11 @@ import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; + import org.springframework.cloud.netflix.ribbon.RibbonLoadBalancedRetryFactory; import org.springframework.cloud.netflix.ribbon.SpringClientFactory; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -56,13 +57,13 @@ public class CachingSpringLoadBalancerFactoryTests { when(this.delegate.getClientConfig("client2")).thenReturn(config); this.factory = new CachingSpringLoadBalancerFactory(this.delegate, - loadBalancedRetryFactory); + this.loadBalancedRetryFactory); } @Test public void delegateCreatesWhenMissing() { FeignLoadBalancer client = this.factory.create("client1"); - assertNotNull("client was null", client); + assertThat(client).as("client was null").isNotNull(); verify(this.delegate, times(1)).getClientConfig("client1"); } @@ -70,10 +71,10 @@ public class CachingSpringLoadBalancerFactoryTests { @Test public void cacheWorks() { FeignLoadBalancer client = this.factory.create("client2"); - assertNotNull("client was null", client); + assertThat(client).as("client was null").isNotNull(); client = this.factory.create("client2"); - assertNotNull("client was null", client); + assertThat(client).as("client was null").isNotNull(); verify(this.delegate, times(1)).getClientConfig("client2"); } @@ -84,9 +85,10 @@ public class CachingSpringLoadBalancerFactoryTests { config.set(CommonClientConfigKey.ConnectTimeout, 1000); config.set(CommonClientConfigKey.ReadTimeout, 500); when(this.delegate.getClientConfig("retry")).thenReturn(config); - CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(this.delegate); + CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory( + this.delegate); FeignLoadBalancer client = this.factory.create("retry"); - assertNotNull("client was null", client); + assertThat(client).as("client was null").isNotNull(); } @Test @@ -95,8 +97,10 @@ public class CachingSpringLoadBalancerFactoryTests { config.set(CommonClientConfigKey.ConnectTimeout, 1000); config.set(CommonClientConfigKey.ReadTimeout, 500); when(this.delegate.getClientConfig("retry")).thenReturn(config); - CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory(this.delegate, loadBalancedRetryFactory); + CachingSpringLoadBalancerFactory factory = new CachingSpringLoadBalancerFactory( + this.delegate, this.loadBalancedRetryFactory); FeignLoadBalancer client = this.factory.create("retry"); - assertNotNull("client was null", client); + assertThat(client).as("client was null").isNotNull(); } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignLoadBalancerTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignLoadBalancerTests.java index 2a285f49..356c73b0 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignLoadBalancerTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignLoadBalancerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.ribbon; @@ -53,8 +52,7 @@ import static com.netflix.client.config.CommonClientConfigKey.ReadTimeout; import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES; import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER; import static feign.Request.HttpMethod.GET; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.eq; @@ -64,8 +62,10 @@ public class FeignLoadBalancerTests { @Mock private Client delegate; + @Mock private ILoadBalancer lb; + @Mock private IClientConfig config; @@ -74,6 +74,7 @@ public class FeignLoadBalancerTests { private ServerIntrospector inspector = new DefaultServerIntrospector(); private Integer defaultConnectTimeout = 10000; + private Integer defaultReadTimeout = 10000; @Before @@ -95,27 +96,19 @@ public class FeignLoadBalancerTests { this.feignLoadBalancer = new FeignLoadBalancer(this.lb, this.config, this.inspector); - Request request = new RequestTemplate() - .method(GET) - .target("http://foo/") - .resolve(new HashMap<>()) - .request(); + Request request = new RequestTemplate().method(GET).target("http://foo/") + .resolve(new HashMap<>()).request(); RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, new URI(request.url())); - Response response = Response.builder() - .request(request) - .status(200) - .reason("Test") - .headers(Collections.emptyMap()) - .body(new byte[0]) - .build(); + Response response = Response.builder().request(request).status(200).reason("Test") + .headers(Collections.emptyMap()).body(new byte[0]).build(); when(this.delegate.execute(any(Request.class), any(Options.class))) .thenReturn(response); RibbonResponse resp = this.feignLoadBalancer.execute(ribbonRequest, null); - assertThat(resp.getRequestedURI(), is(new URI("http://foo"))); + assertThat(resp.getRequestedURI()).isEqualTo(new URI("http://foo")); } @Test @@ -126,7 +119,7 @@ public class FeignLoadBalancerTests { Server server = new Server("foo", 7777); URI uri = this.feignLoadBalancer.reconstructURIWithServer(server, new URI("http://foo/")); - assertThat(uri, is(new URI("https://foo:7777/"))); + assertThat(uri).isEqualTo(new URI("https://foo:7777/")); } @Test @@ -148,7 +141,7 @@ public class FeignLoadBalancerTests { Server server = new Server("foo", 7777); URI uri = this.feignLoadBalancer.reconstructURIWithServer(server, new URI("http://foo/")); - assertThat(uri, is(new URI("http://foo:7777/"))); + assertThat(uri).isEqualTo(new URI("http://foo:7777/")); } @Test @@ -160,21 +153,22 @@ public class FeignLoadBalancerTests { when(server.getHost()).thenReturn("foo"); URI uri = this.feignLoadBalancer.reconstructURIWithServer(server, new URI("http://bar/")); - assertThat(uri, is(new URI("https://foo:443/"))); + assertThat(uri).isEqualTo(new URI("https://foo:443/")); } @Test public void testRibbonRequestURLEncode() throws Exception { - String url = "http://foo/?name=%7bcookie";//name={cookie + String url = "http://foo/?name=%7bcookie"; // name={cookie Request request = Request.create(GET, url, new HashMap<>(), null, null); - assertThat(request.url(),is(url)); + assertThat(request.url()).isEqualTo(url); - RibbonRequest ribbonRequest = new RibbonRequest(this.delegate,request,new URI(request.url())); + RibbonRequest ribbonRequest = new RibbonRequest(this.delegate, request, + new URI(request.url())); Request cloneRequest = ribbonRequest.toRequest(); - assertThat(cloneRequest.url(),is(url)); + assertThat(cloneRequest.url()).isEqualTo(url); } @@ -193,23 +187,23 @@ public class FeignLoadBalancerTests { this.feignLoadBalancer = new FeignLoadBalancer(baseLoadBalancer, this.config, this.inspector) { - protected void customizeLoadBalancerCommandBuilder(final FeignLoadBalancer.RibbonRequest request, final IClientConfig config, - final LoadBalancerCommand.Builder builder) { + protected void customizeLoadBalancerCommandBuilder( + final FeignLoadBalancer.RibbonRequest request, + final IClientConfig config, + final LoadBalancerCommand.Builder builder) { builder.withServerLocator(request.getRequest().headers().get("c_ip")); } }; - Request request = new RequestTemplate().method(GET).resolve(new HashMap<>()).request(); - RibbonResponse resp = this.feignLoadBalancer.executeWithLoadBalancer(new RibbonRequest(this.delegate, request, - new URI(request.url())), null); - assertThat(resp.getRequestedURI().getPort(), is(7777)); - request = new RequestTemplate() - .method(GET) - .header("c_ip", "666") - .resolve(new HashMap<>()) + Request request = new RequestTemplate().method(GET).resolve(new HashMap<>()) .request(); - resp = this.feignLoadBalancer.executeWithLoadBalancer(new RibbonRequest(this.delegate, request, - new URI(request.url())), null); - assertThat(resp.getRequestedURI().getPort(), is(6666)); + RibbonResponse resp = this.feignLoadBalancer.executeWithLoadBalancer( + new RibbonRequest(this.delegate, request, new URI(request.url())), null); + assertThat(resp.getRequestedURI().getPort()).isEqualTo(7777); + request = new RequestTemplate().method(GET).header("c_ip", "666") + .resolve(new HashMap<>()).request(); + resp = this.feignLoadBalancer.executeWithLoadBalancer( + new RibbonRequest(this.delegate, request, new URI(request.url())), null); + assertThat(resp.getRequestedURI().getPort()).isEqualTo(6666); } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientPathTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientPathTests.java index b3d57fcc..7741c467 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientPathTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientPathTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,7 +25,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -40,21 +39,18 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * @author Venil Noronha */ @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = FeignRibbonClientPathTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT,value = { - "spring.application.name=feignribbonclientpathtest", - "feign.okhttp.enabled=false", - "feign.httpclient.enabled=false", - "feign.hystrix.enabled=false", +@SpringBootTest(classes = FeignRibbonClientPathTests.Application.class, webEnvironment = RANDOM_PORT, value = { + "spring.application.name=feignribbonclientpathtest", "feign.okhttp.enabled=false", + "feign.httpclient.enabled=false", "feign.hystrix.enabled=false", "test.path.prefix=/base/path" // For pathWithPlaceholder test - } -) +}) @DirtiesContext public class FeignRibbonClientPathTests { @@ -72,51 +68,10 @@ public class FeignRibbonClientPathTests { @Autowired private TestClient4 testClient4; - + @Autowired private TestClient5 testClient5; - protected interface TestClient { - - @RequestMapping(method = RequestMethod.GET, value = "/hello") - Hello getHello(); - - } - - @FeignClient(name = "localapp", path = "/base/path") - protected interface TestClient1 extends TestClient { } - - @FeignClient(name = "localapp1", path = "base/path") - protected interface TestClient2 extends TestClient { } - - @FeignClient(name = "localapp2", path = "base/path/") - protected interface TestClient3 extends TestClient { } - - @FeignClient(name = "localapp3", path = "/base/path/") - protected interface TestClient4 extends TestClient { } - - @FeignClient(name = "localapp4", path = "${test.path.prefix}") - protected interface TestClient5 extends TestClient { } - - @Configuration - @EnableAutoConfiguration - @RestController - @RequestMapping("/base/path") - @EnableFeignClients(clients = { - TestClient1.class, TestClient2.class, TestClient3.class, TestClient4.class, - TestClient5.class - }) - @RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class) - @Import(NoSecurityConfiguration.class) - public static class Application { - - @RequestMapping(method = RequestMethod.GET, value = "/hello") - public Hello getHello() { - return new Hello("hello world"); - } - - } - @Test public void pathWithLeadingButNotTrailingSlash() { testClientPath(this.testClient1); @@ -144,27 +99,79 @@ public class FeignRibbonClientPathTests { private void testClientPath(TestClient testClient) { Hello hello = testClient.getHello(); - assertNotNull("Object returned was null", hello); - assertEquals("Response object value didn't match", "hello world", - hello.getMessage()); + assertThat(hello).as("Object returned was null").isNotNull(); + assertThat(hello.getMessage()).as("Response object value didn't match") + .isEqualTo("hello world"); + } + + protected interface TestClient { + + @RequestMapping(method = RequestMethod.GET, value = "/hello") + Hello getHello(); + + } + + @FeignClient(name = "localapp", path = "/base/path") + protected interface TestClient1 extends TestClient { + + } + + @FeignClient(name = "localapp1", path = "base/path") + protected interface TestClient2 extends TestClient { + + } + + @FeignClient(name = "localapp2", path = "base/path/") + protected interface TestClient3 extends TestClient { + + } + + @FeignClient(name = "localapp3", path = "/base/path/") + protected interface TestClient4 extends TestClient { + + } + + @FeignClient(name = "localapp4", path = "${test.path.prefix}") + protected interface TestClient5 extends TestClient { + + } + + @Configuration + @EnableAutoConfiguration + @RestController + @RequestMapping("/base/path") + @EnableFeignClients(clients = { TestClient1.class, TestClient2.class, + TestClient3.class, TestClient4.class, TestClient5.class }) + @RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class) + @Import(NoSecurityConfiguration.class) + public static class Application { + + @RequestMapping(method = RequestMethod.GET, value = "/hello") + public Hello getHello() { + return new Hello("hello world"); + } + } public static class Hello { + private String message; - public Hello() {} + public Hello() { + } public Hello(String message) { this.message = message; } public String getMessage() { - return message; + return this.message; } public void setMessage(String message) { this.message = message; } + } @Configuration diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientRetryTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientRetryTests.java index f39a5512..7dd6a4b6 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientRetryTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientRetryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,17 +20,19 @@ import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.concurrent.atomic.AtomicInteger; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.StaticServerList; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -41,22 +43,20 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * Tests the Feign Retryer, not ribbon retry. + * * @author Spencer Gibb */ @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = FeignRibbonClientRetryTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { +@SpringBootTest(classes = FeignRibbonClientRetryTests.Application.class, webEnvironment = RANDOM_PORT, value = { "spring.application.name=feignclientretrytest", "feign.okhttp.enabled=false", - "feign.httpclient.enabled=false", "feign.hystrix.enabled=false", "localapp.ribbon.MaxAutoRetries=2", - "localapp.ribbon.MaxAutoRetriesNextServer=3"}) + "feign.httpclient.enabled=false", "feign.hystrix.enabled=false", + "localapp.ribbon.MaxAutoRetries=2", + "localapp.ribbon.MaxAutoRetriesNextServer=3" }) @DirtiesContext public class FeignRibbonClientRetryTests { @@ -66,13 +66,32 @@ public class FeignRibbonClientRetryTests { @Autowired private TestClient testClient; + @Test + public void testClient() { + assertThat(this.testClient).as("testClient was null").isNotNull(); + assertThat(Proxy.isProxyClass(this.testClient.getClass())) + .as("testClient is not a java Proxy").isTrue(); + InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient); + assertThat(invocationHandler).as("invocationHandler was null").isNotNull(); + } + + @Test + public void testRetries() { + int retryMe = this.testClient.retryMe(); + assertThat(1).as("retryCount didn't match").isEqualTo(retryMe); + // TODO: not sure how to verify retry happens. Debugging through it, it works + // maybe the assertEquals above is enough because of the bogus servers + } + @FeignClient("localapp") protected interface TestClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello getHello(); @RequestMapping(method = RequestMethod.GET, value = "/retryme") int retryMe(); + } @Configuration @@ -94,27 +113,11 @@ public class FeignRibbonClientRetryTests { public int retryMe() { return this.retries.getAndIncrement(); } - - } - @Test - public void testClient() { - assertNotNull("testClient was null", this.testClient); - assertTrue("testClient is not a java Proxy", - Proxy.isProxyClass(this.testClient.getClass())); - InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient); - assertNotNull("invocationHandler was null", invocationHandler); - } - - @Test - public void testRetries() { - int retryMe = this.testClient.retryMe(); - assertEquals("retryCount didn't match", retryMe, 1); - // TODO: not sure how to verify retry happens. Debugging through it, it works - // maybe the assertEquals above is enough because of the bogus servers } public static class Hello { + private String message; public Hello() { @@ -125,13 +128,15 @@ public class FeignRibbonClientRetryTests { } public String getMessage() { - return message; + return this.message; } public void setMessage(String message) { this.message = message; } + } + } // Load balancer with fixed server list for "local" pointing to localhost @@ -145,8 +150,8 @@ class LocalRibbonClientConfiguration { @Bean public ServerList ribbonServerList() { return new StaticServerList<>(new Server("mybadhost", 80), - new Server("mybadhost2", 10002), - new Server("mybadhost3", 10003), new Server("localhost", this.port)); + new Server("mybadhost2", 10002), new Server("mybadhost3", 10003), + new Server("localhost", this.port)); } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientTests.java index 9e6b4ccd..f2c347a6 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,6 +52,7 @@ import static org.mockito.hamcrest.MockitoHamcrest.argThat; public class FeignRibbonClientTests { private AbstractLoadBalancer loadBalancer = mock(AbstractLoadBalancer.class); + private Client delegate = mock(Client.class); private SpringClientFactory factory = new SpringClientFactory() { @@ -81,71 +82,58 @@ public class FeignRibbonClientTests { // Even though we don't maintain FeignRibbonClient, keep these tests // around to make sure the expected behaviour doesn't break - private Client client = new LoadBalancerFeignClient(this.delegate, new CachingSpringLoadBalancerFactory(this.factory), this.factory); + private Client client = new LoadBalancerFeignClient(this.delegate, + new CachingSpringLoadBalancerFactory(this.factory), this.factory); @Before public void init() { - when(this.loadBalancer.chooseServer(any())).thenReturn( - new Server("foo.com", 8000)); - //to fix NPE + when(this.loadBalancer.chooseServer(any())) + .thenReturn(new Server("foo.com", 8000)); + // to fix NPE LoadBalancerStats stats = mock(LoadBalancerStats.class); when(this.loadBalancer.getLoadBalancerStats()).thenReturn(stats); - when(stats.getSingleServerStat(any(Server.class))).thenReturn(mock(ServerStats.class)); + when(stats.getSingleServerStat(any(Server.class))) + .thenReturn(mock(ServerStats.class)); } @Test public void remoteRequestIsSentAtRoot() throws Exception { - Request request = new RequestTemplate() - .method(GET) - .target("http://foo") - .resolve(new HashMap<>()) - .request(); + Request request = new RequestTemplate().method(GET).target("http://foo") + .resolve(new HashMap<>()).request(); this.client.execute(request, new Options()); RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/"); - verify(this.delegate).execute(argThat(matcher), - any(Options.class)); + verify(this.delegate).execute(argThat(matcher), any(Options.class)); } @Test public void remoteRequestIsSent() throws Exception { - Request request = new RequestTemplate() - .method(GET) - .target("http://foo/") - .resolve(new HashMap<>()) - .request(); + Request request = new RequestTemplate().method(GET).target("http://foo/") + .resolve(new HashMap<>()).request(); this.client.execute(request, new Options()); RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/"); - verify(this.delegate).execute(argThat(matcher), - any(Options.class)); + verify(this.delegate).execute(argThat(matcher), any(Options.class)); } @Test public void verifyCleanUrl() throws Exception { - Request request = new RequestTemplate() - .method(GET) - .target("http://tp/abc/bcd.json") - .resolve(new HashMap<>()) - .request(); + Request request = new RequestTemplate().method(GET) + .target("http://tp/abc/bcd.json").resolve(new HashMap<>()).request(); this.client.execute(request, new Options()); RequestMatcher matcher = new RequestMatcher("http://foo.com:8000/abc/bcd.json"); - verify(this.delegate).execute(argThat(matcher), - any(Options.class)); + verify(this.delegate).execute(argThat(matcher), any(Options.class)); } @Test public void remoteRequestIsSecure() throws Exception { - Request request = new RequestTemplate() - .method(GET) - .target("https://foo/") - .resolve(new HashMap<>()) - .request(); + Request request = new RequestTemplate().method(GET).target("https://foo/") + .resolve(new HashMap<>()).request(); this.client.execute(request, new Options()); RequestMatcher matcher = new RequestMatcher("https://foo.com:8000/"); - verify(this.delegate).execute(argThat(matcher), - any(Options.class)); + verify(this.delegate).execute(argThat(matcher), any(Options.class)); } private final static class RequestMatcher extends CustomMatcher { + private String url; private RequestMatcher(String url) { @@ -158,6 +146,7 @@ public class FeignRibbonClientTests { Request request = (Request) item; return request.url().equals(this.url); } + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonHttpClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonHttpClientConfigurationTests.java index 33f6f695..1376627c 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonHttpClientConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonHttpClientConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,40 +12,40 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.openfeign.ribbon; import java.lang.reflect.Field; + import javax.net.ssl.SSLContextSpi; import javax.net.ssl.SSLSocketFactory; import javax.net.ssl.X509TrustManager; + import org.apache.http.config.Lookup; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.conn.socket.ConnectionSocketFactory; import org.apache.http.impl.conn.DefaultHttpClientConnectionOperator; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientRetryTests; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.ReflectionUtils; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Ryan Baxter */ @RunWith(SpringRunner.class) -@SpringBootTest(classes = FeignRibbonHttpClientConfigurationTests.FeignRibbonHttpClientConfigurationTestsApplication.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = {"debug=true","feign.httpclient.disableSslValidation=true"}) +@SpringBootTest(classes = FeignRibbonHttpClientConfigurationTests.FeignRibbonHttpClientConfigurationTestsApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "debug=true", "feign.httpclient.disableSslValidation=true" }) @DirtiesContext public class FeignRibbonHttpClientConfigurationTests { @@ -54,21 +54,29 @@ public class FeignRibbonHttpClientConfigurationTests { @Test public void disableSslTest() throws Exception { - Lookup socketFactoryRegistry = getConnectionSocketFactoryLookup(connectionManager); - assertNotNull(socketFactoryRegistry.lookup("https")); - assertNull(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers()); + Lookup socketFactoryRegistry = getConnectionSocketFactoryLookup( + this.connectionManager); + assertThat(socketFactoryRegistry.lookup("https")).isNotNull(); + assertThat(this.getX509TrustManager(socketFactoryRegistry).getAcceptedIssuers()) + .isNull(); } - private Lookup getConnectionSocketFactoryLookup(HttpClientConnectionManager connectionManager) { - DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator)this.getField(connectionManager, "connectionOperator"); - return (Lookup)this.getField(connectionOperator, "socketFactoryRegistry"); + private Lookup getConnectionSocketFactoryLookup( + HttpClientConnectionManager connectionManager) { + DefaultHttpClientConnectionOperator connectionOperator = (DefaultHttpClientConnectionOperator) this + .getField(connectionManager, "connectionOperator"); + return (Lookup) this.getField(connectionOperator, "socketFactoryRegistry"); } - private X509TrustManager getX509TrustManager(Lookup socketFactoryRegistry) { - ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory)socketFactoryRegistry.lookup("https"); - SSLSocketFactory sslSocketFactory = (SSLSocketFactory)this.getField(connectionSocketFactory, "socketfactory"); - SSLContextSpi sslContext = (SSLContextSpi)this.getField(sslSocketFactory, "context"); - return (X509TrustManager)this.getField(sslContext, "trustManager"); + private X509TrustManager getX509TrustManager( + Lookup socketFactoryRegistry) { + ConnectionSocketFactory connectionSocketFactory = (ConnectionSocketFactory) socketFactoryRegistry + .lookup("https"); + SSLSocketFactory sslSocketFactory = (SSLSocketFactory) this + .getField(connectionSocketFactory, "socketfactory"); + SSLContextSpi sslContext = (SSLContextSpi) this.getField(sslSocketFactory, + "context"); + return (X509TrustManager) this.getField(sslContext, "trustManager"); } protected Object getField(Object target, String name) { @@ -81,9 +89,12 @@ public class FeignRibbonHttpClientConfigurationTests { @Configuration @EnableAutoConfiguration static class FeignRibbonHttpClientConfigurationTestsApplication { + public static void main(String[] args) { new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class) .run(args); } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonOkHttpClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonOkHttpClientConfigurationTests.java index 3ebaeb82..2be63ce0 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonOkHttpClientConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/FeignRibbonOkHttpClientConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,36 +12,37 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ + package org.springframework.cloud.openfeign.ribbon; -import okhttp3.OkHttpClient; - import java.lang.reflect.Field; + import javax.net.ssl.HostnameVerifier; -import org.junit.Assert; + +import okhttp3.OkHttpClient; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.cloud.commons.httpclient.OkHttpClientFactory; -import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientRetryTests; import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.ReflectionUtils; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Ryan Baxter */ @RunWith(SpringRunner.class) -@SpringBootTest(classes = FeignRibbonOkHttpClientConfigurationTests.FeignRibbonOkHttpClientConfigurationTestsApplication.class, - webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, - properties = {"debug=true","feign.httpclient.disableSslValidation=true", - "feign.okhttp.enabled=true", "feign.httpclient.enabled=false"}) +@SpringBootTest(classes = FeignRibbonOkHttpClientConfigurationTests.FeignRibbonOkHttpClientConfigurationTestsApplication.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, properties = { + "debug=true", "feign.httpclient.disableSslValidation=true", + "feign.okhttp.enabled=true", "feign.httpclient.enabled=false" }) @DirtiesContext public class FeignRibbonOkHttpClientConfigurationTests { @@ -50,8 +51,11 @@ public class FeignRibbonOkHttpClientConfigurationTests { @Test public void disableSslTest() throws Exception { - HostnameVerifier hostnameVerifier = (HostnameVerifier)this.getField(httpClient, "hostnameVerifier"); - Assert.assertTrue(OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier)); + HostnameVerifier hostnameVerifier = (HostnameVerifier) this + .getField(this.httpClient, "hostnameVerifier"); + assertThat( + OkHttpClientFactory.TrustAllHostnames.class.isInstance(hostnameVerifier)) + .isTrue(); } protected Object getField(Object target, String name) { @@ -64,9 +68,12 @@ public class FeignRibbonOkHttpClientConfigurationTests { @Configuration @EnableAutoConfiguration static class FeignRibbonOkHttpClientConfigurationTestsApplication { + public static void main(String[] args) { new SpringApplicationBuilder(FeignRibbonClientRetryTests.Application.class) .run(args); } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClientOverrideTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClientOverrideTests.java index 7c8c27cb..838ffd57 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClientOverrideTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/LoadBalancerFeignClientOverrideTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,10 +16,12 @@ package org.springframework.cloud.openfeign.ribbon; -import static org.junit.Assert.assertEquals; - +import com.netflix.client.config.CommonClientConfigKey; +import com.netflix.client.config.IClientConfig; +import feign.Request; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; @@ -33,10 +35,7 @@ import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.web.bind.annotation.RequestMapping; -import com.netflix.client.config.CommonClientConfigKey; -import com.netflix.client.config.IClientConfig; - -import feign.Request; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb @@ -56,20 +55,20 @@ public class LoadBalancerFeignClientOverrideTests { // specific ribbon 'bar' configuration via spring bean Request.Options barOptions = this.context.getInstance("bar", Request.Options.class); - assertEquals(1, barOptions.connectTimeoutMillis()); - assertEquals(2, barOptions.readTimeoutMillis()); + assertThat(barOptions.connectTimeoutMillis()).isEqualTo(1); + assertThat(barOptions.readTimeoutMillis()).isEqualTo(2); assertOptions(barOptions, "bar", 1, 2); // specific ribbon 'foo' configuration via application.yml Request.Options fooOptions = this.context.getInstance("foo", Request.Options.class); - assertEquals(LoadBalancerFeignClient.DEFAULT_OPTIONS, fooOptions); + assertThat(fooOptions).isEqualTo(LoadBalancerFeignClient.DEFAULT_OPTIONS); assertOptions(fooOptions, "foo", 7, 17); // generic ribbon default configuration Request.Options bazOptions = this.context.getInstance("baz", Request.Options.class); - assertEquals(LoadBalancerFeignClient.DEFAULT_OPTIONS, bazOptions); + assertThat(bazOptions).isEqualTo(LoadBalancerFeignClient.DEFAULT_OPTIONS); assertOptions(bazOptions, "baz", 3001, 60001); } @@ -78,44 +77,54 @@ public class LoadBalancerFeignClientOverrideTests { LoadBalancerFeignClient client = this.context.getInstance(name, LoadBalancerFeignClient.class); IClientConfig config = client.getClientConfig(options, name); - assertEquals("connect was wrong for " + name, expectedConnect, - config.get(CommonClientConfigKey.ConnectTimeout, -1).intValue()); - assertEquals("read was wrong for " + name, expectedRead, - config.get(CommonClientConfigKey.ReadTimeout, -1).intValue()); + assertThat(config.get(CommonClientConfigKey.ConnectTimeout, -1).intValue()) + .as("connect was wrong for " + name).isEqualTo(expectedConnect); + assertThat(config.get(CommonClientConfigKey.ReadTimeout, -1).intValue()) + .as("read was wrong for " + name).isEqualTo(expectedRead); + } + + @FeignClient(value = "foo", configuration = FooConfiguration.class) + interface FooClient { + + @RequestMapping("/") + String get(); + + } + + @FeignClient(value = "bar", configuration = BarConfiguration.class) + interface BarClient { + + @RequestMapping("/") + String get(); + + } + + @FeignClient("baz") + interface BazClient { + + @RequestMapping("/") + String get(); + } @Configuration @EnableFeignClients(clients = { FooClient.class, BarClient.class, BazClient.class }) @EnableAutoConfiguration protected static class TestConfiguration { - } - - @FeignClient(value = "foo", configuration = FooConfiguration.class) - interface FooClient { - @RequestMapping("/") - String get(); } public static class FooConfiguration { - } - @FeignClient(value = "bar", configuration = BarConfiguration.class) - interface BarClient { - @RequestMapping("/") - String get(); } public static class BarConfiguration { + @Bean public Request.Options feignRequestOptions() { return new Request.Options(1, 2); } + } - @FeignClient("baz") - interface BazClient { - @RequestMapping("/") - String get(); - } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancerTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancerTests.java index 32b3add0..38299017 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancerTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/RetryableFeignLoadBalancerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.ribbon; @@ -36,7 +35,6 @@ import com.netflix.loadbalancer.Server; import feign.Client; import feign.Request; import feign.Response; -import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; @@ -69,10 +67,7 @@ import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO import static com.netflix.client.config.DefaultClientConfigImpl.DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER; import static feign.Request.HttpMethod.GET; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; @@ -90,13 +85,17 @@ import static org.mockito.Mockito.when; * @author Olga Maciaszek-Sharma */ public class RetryableFeignLoadBalancerTests { + @Mock private ILoadBalancer lb; + @Mock private IClientConfig config; + private ServerIntrospector inspector = new DefaultServerIntrospector(); private Integer defaultConnectTimeout = 10000; + private Integer defaultReadTimeout = 10000; @Before @@ -114,31 +113,38 @@ public class RetryableFeignLoadBalancerTests { @Test public void executeNoFailure() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); SpringClientFactory clientFactory = mock(SpringClientFactory.class); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); IClientConfig config = mock(IClientConfig.class); doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); - doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); - doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); - doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout)); - doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout)); - doReturn("404,502,foo, ,").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq("")); + doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), + anyInt()); + doReturn(true).when(config) + .get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); + doReturn(this.defaultConnectTimeout).when(config) + .get(eq(CommonClientConfigKey.ConnectTimeout)); + doReturn(this.defaultReadTimeout).when(config) + .get(eq(CommonClientConfigKey.ReadTimeout)); + doReturn("404,502,foo, ,").when(config).getPropertyAsString( + eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); doReturn(config).when(clientFactory).getClientConfig(eq("default")); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory); + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory); Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo")); - Response response = Response.builder() - .status(200) - .request(feignRequest) - .headers(new HashMap<>()) - .build(); - doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory); + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://foo")); + Response response = Response.builder().status(200).request(feignRequest) + .headers(new HashMap<>()).build(); + doReturn(response).when(client).execute(any(Request.class), + any(Request.Options.class)); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + config, this.inspector, loadBalancedRetryFactory); FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); - assertEquals(200, ribbonResponse.toResponse().status()); + assertThat(ribbonResponse.toResponse().status()).isEqualTo(200); verify(client, times(1)).execute(any(Request.class), any(Request.Options.class)); } @@ -147,48 +153,62 @@ public class RetryableFeignLoadBalancerTests { Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo")); - doThrow(new IOException("boom")).when(client).execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, new LoadBalancedRetryFactory() { - @Override - public LoadBalancedRetryPolicy createRetryPolicy(String s, ServiceInstanceChooser serviceInstanceChooser) { - return null; - } + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://foo")); + doThrow(new IOException("boom")).when(client).execute(any(Request.class), + any(Request.Options.class)); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + this.config, this.inspector, new LoadBalancedRetryFactory() { + @Override + public LoadBalancedRetryPolicy createRetryPolicy(String s, + ServiceInstanceChooser serviceInstanceChooser) { + return null; + } - @Override - public RetryListener[] createRetryListeners(String service) { - return new RetryListener[0]; - } + @Override + public RetryListener[] createRetryListeners(String service) { + return new RetryListener[0]; + } - @Override - public BackOffPolicy createBackOffPolicy(String service) { - return null; - } - }); + @Override + public BackOffPolicy createBackOffPolicy(String service) { + return null; + } + }); try { feignLb.execute(request, null); - } catch(Exception e) { - assertThat(e, instanceOf(IOException.class)); - } finally { - verify(client, times(1)).execute(any(Request.class), any(Request.Options.class)); + } + catch (Exception e) { + assertThat(e).isInstanceOf(IOException.class); + } + finally { + verify(client, times(1)).execute(any(Request.class), + any(Request.Options.class)); } } @Test public void executeRetry() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); SpringClientFactory clientFactory = mock(SpringClientFactory.class); IClientConfig config = mock(IClientConfig.class); doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); - doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); - doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); - doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout)); - doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout)); - doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq("")); + doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), + anyInt()); + doReturn(true).when(config) + .get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); + doReturn(this.defaultConnectTimeout).when(config) + .get(eq(CommonClientConfigKey.ConnectTimeout)); + doReturn(this.defaultReadTimeout).when(config) + .get(eq(CommonClientConfigKey.ReadTimeout)); + doReturn("").when(config).getPropertyAsString( + eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); doReturn(config).when(clientFactory).getClientConfig(eq("default")); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); MyBackOffPolicy backOffPolicy = new MyBackOffPolicy(); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){ + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory) { @Override public BackOffPolicy createBackOffPolicy(String service) { return backOffPolicy; @@ -197,36 +217,43 @@ public class RetryableFeignLoadBalancerTests { Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo")); - Response response = Response.builder() - .status(200) - .request(feignRequest) - .headers(new HashMap<>()) - .build(); - doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class)); + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://foo")); + Response response = Response.builder().status(200).request(feignRequest) + .headers(new HashMap<>()).build(); + doThrow(new IOException("boom")).doReturn(response).when(client) + .execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + config, this.inspector, loadBalancedRetryFactory); FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); - assertEquals(200, ribbonResponse.toResponse().status()); + assertThat(ribbonResponse.toResponse().status()).isEqualTo(200); verify(client, times(2)).execute(any(Request.class), any(Request.Options.class)); - assertEquals(1, backOffPolicy.getCount()); + assertThat(backOffPolicy.getCount()).isEqualTo(1); } @Test public void executeRetryOnStatusCode() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); SpringClientFactory clientFactory = mock(SpringClientFactory.class); IClientConfig config = mock(IClientConfig.class); doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); - doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); - doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); - doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout)); - doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout)); - doReturn("404").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq("")); + doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), + anyInt()); + doReturn(true).when(config) + .get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); + doReturn(this.defaultConnectTimeout).when(config) + .get(eq(CommonClientConfigKey.ConnectTimeout)); + doReturn(this.defaultReadTimeout).when(config) + .get(eq(CommonClientConfigKey.ReadTimeout)); + doReturn("404").when(config).getPropertyAsString( + eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); doReturn(config).when(clientFactory).getClientConfig(eq("default")); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); MyBackOffPolicy backOffPolicy = new MyBackOffPolicy(); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){ + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory) { @Override public BackOffPolicy createBackOffPolicy(String service) { return backOffPolicy; @@ -235,168 +262,182 @@ public class RetryableFeignLoadBalancerTests { Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo")); - Response response = Response - .builder() - .request(feignRequest) - .status(200) - .headers(new HashMap<>()) - .build(); - Response fourOFourResponse = Response.builder() - .request(feignRequest) - .status(404) + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://foo")); + Response response = Response.builder().request(feignRequest).status(200) .headers(new HashMap<>()).build(); - doReturn(fourOFourResponse).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory); + Response fourOFourResponse = Response.builder().request(feignRequest).status(404) + .headers(new HashMap<>()).build(); + doReturn(fourOFourResponse).doReturn(response).when(client) + .execute(any(Request.class), any(Request.Options.class)); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + config, this.inspector, loadBalancedRetryFactory); FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); - assertEquals(200, ribbonResponse.toResponse().status()); + assertThat(ribbonResponse.toResponse().status()).isEqualTo(200); verify(client, times(2)).execute(any(Request.class), any(Request.Options.class)); - assertEquals(1, backOffPolicy.getCount()); + assertThat(backOffPolicy.getCount()).isEqualTo(1); } - @Test + @Test public void executeRetryOnStatusCodeWithEmptyBody() throws Exception { - int retriesNextServer = 0; - when(this.config.get(MaxAutoRetriesNextServer, - DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(retriesNextServer); - doReturn(new Server("foo", 80)).when(lb).chooseServer(any()); - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); - SpringClientFactory clientFactory = mock(SpringClientFactory.class); - IClientConfig config = mock(IClientConfig.class); - doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); - doReturn(retriesNextServer).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); - doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); - doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout)); - doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout)); - doReturn("404").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq("")); - doReturn(config).when(clientFactory).getClientConfig(eq("default")); - doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); - MyBackOffPolicy backOffPolicy = new MyBackOffPolicy(); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){ - @Override - public BackOffPolicy createBackOffPolicy(String service) { - return backOffPolicy; - } - }; - Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), - new byte[] {}, UTF_8); - Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo")); - Response response = Response.builder() - .request(feignRequest) - .status(404) - .headers(new HashMap<>()) - .build(); - Response fourOFourResponse = Response.builder() - .request(feignRequest) - .status(404) - .headers(new HashMap<>()) - .build(); - doReturn(fourOFourResponse).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory); - FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); - assertEquals(404, ribbonResponse.toResponse().status()); - assertEquals(Integer.valueOf(0), ribbonResponse.toResponse().body().length()); - verify(client, times(2)).execute(any(Request.class), any(Request.Options.class)); - assertEquals(1, backOffPolicy.getCount()); + int retriesNextServer = 0; + when(this.config.get(MaxAutoRetriesNextServer, + DEFAULT_MAX_AUTO_RETRIES_NEXT_SERVER)).thenReturn(retriesNextServer); + doReturn(new Server("foo", 80)).when(this.lb).chooseServer(any()); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); + SpringClientFactory clientFactory = mock(SpringClientFactory.class); + IClientConfig config = mock(IClientConfig.class); + doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); + doReturn(retriesNextServer).when(config) + .get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); + doReturn(true).when(config) + .get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); + doReturn(this.defaultConnectTimeout).when(config) + .get(eq(CommonClientConfigKey.ConnectTimeout)); + doReturn(this.defaultReadTimeout).when(config) + .get(eq(CommonClientConfigKey.ReadTimeout)); + doReturn("404").when(config).getPropertyAsString( + eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); + doReturn(config).when(clientFactory).getClientConfig(eq("default")); + doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); + MyBackOffPolicy backOffPolicy = new MyBackOffPolicy(); + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory) { + @Override + public BackOffPolicy createBackOffPolicy(String service) { + return backOffPolicy; + } + }; + Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), + new byte[] {}, UTF_8); + Client client = mock(Client.class); + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://foo")); + Response response = Response.builder().request(feignRequest).status(404) + .headers(new HashMap<>()).build(); + Response fourOFourResponse = Response.builder().request(feignRequest).status(404) + .headers(new HashMap<>()).build(); + doReturn(fourOFourResponse).doReturn(response).when(client) + .execute(any(Request.class), any(Request.Options.class)); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + config, this.inspector, loadBalancedRetryFactory); + FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); + assertThat(ribbonResponse.toResponse().status()).isEqualTo(404); + assertThat(ribbonResponse.toResponse().body().length()) + .isEqualTo(Integer.valueOf(0)); + verify(client, times(2)).execute(any(Request.class), any(Request.Options.class)); + assertThat(backOffPolicy.getCount()).isEqualTo(1); } @Test public void getRequestSpecificRetryHandler() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); SpringClientFactory clientFactory = mock(SpringClientFactory.class); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory); + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory); Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo")); - Response response = Response.builder() - .request(feignRequest) - .status(200) - .headers(new HashMap<>()) - .build(); - doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory); - RequestSpecificRetryHandler retryHandler = feignLb.getRequestSpecificRetryHandler(request, config); - assertEquals(1, retryHandler.getMaxRetriesOnNextServer()); - assertEquals(1, retryHandler.getMaxRetriesOnSameServer()); + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://foo")); + Response response = Response.builder().request(feignRequest).status(200) + .headers(new HashMap<>()).build(); + doReturn(response).when(client).execute(any(Request.class), + any(Request.Options.class)); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + this.config, this.inspector, loadBalancedRetryFactory); + RequestSpecificRetryHandler retryHandler = feignLb + .getRequestSpecificRetryHandler(request, this.config); + assertThat(retryHandler.getMaxRetriesOnNextServer()).isEqualTo(1); + assertThat(retryHandler.getMaxRetriesOnSameServer()).isEqualTo(1); } @Test public void choose() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); SpringClientFactory clientFactory = mock(SpringClientFactory.class); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory); - Request feignRequest = Request - .create(GET, "http://foo", new HashMap<>(), - new byte[] {}, UTF_8); + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory); + Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), + new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo")); - Response response = Response.builder() - .request(feignRequest) - .status(200).headers(new HashMap<>()) - .build(); - doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class)); + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://foo")); + Response response = Response.builder().request(feignRequest).status(200) + .headers(new HashMap<>()).build(); + doReturn(response).when(client).execute(any(Request.class), + any(Request.Options.class)); final Server server = new Server("foo", 80); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(new ILoadBalancer() { - @Override - public void addServers(List list) { + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer( + new ILoadBalancer() { + @Override + public void addServers(List list) { - } + } - @Override - public Server chooseServer(Object o) { - return server; - } + @Override + public Server chooseServer(Object o) { + return server; + } - @Override - public void markServerDown(Server server) { + @Override + public void markServerDown(Server server) { - } + } - @Override - public List getServerList(boolean b) { - return null; - } + @Override + public List getServerList(boolean b) { + return null; + } - @Override - public List getReachableServers() { - return null; - } + @Override + public List getReachableServers() { + return null; + } - @Override - public List getAllServers() { - return null; - } - }, config, inspector, loadBalancedRetryFactory); + @Override + public List getAllServers() { + return null; + } + }, this.config, this.inspector, loadBalancedRetryFactory); ServiceInstance serviceInstance = feignLb.choose("foo"); - assertEquals("foo", serviceInstance.getHost()); - assertEquals(80, serviceInstance.getPort()); + assertThat(serviceInstance.getHost()).isEqualTo("foo"); + assertThat(serviceInstance.getPort()).isEqualTo(80); } @Test public void retryListenerTest() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); SpringClientFactory clientFactory = mock(SpringClientFactory.class); IClientConfig config = mock(IClientConfig.class); doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); - doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); - doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); - doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout)); - doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout)); - doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq("")); + doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), + anyInt()); + doReturn(true).when(config) + .get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); + doReturn(this.defaultConnectTimeout).when(config) + .get(eq(CommonClientConfigKey.ConnectTimeout)); + doReturn(this.defaultReadTimeout).when(config) + .get(eq(CommonClientConfigKey.ReadTimeout)); + doReturn("").when(config).getPropertyAsString( + eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); doReturn(config).when(clientFactory).getClientConfig(eq("default")); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); MyBackOffPolicy backOffPolicy = new MyBackOffPolicy(); MyRetryListener myRetryListener = new MyRetryListener(); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory) { + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory) { @Override public RetryListener[] createRetryListeners(String service) { - return new RetryListener[]{myRetryListener}; + return new RetryListener[] { myRetryListener }; } @Override @@ -407,41 +448,48 @@ public class RetryableFeignLoadBalancerTests { Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener")); - Response response = Response.builder() - .request(feignRequest) - .status(200) - .headers(new HashMap<>()) - .build(); - doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class)); + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://listener")); + Response response = Response.builder().request(feignRequest).status(200) + .headers(new HashMap<>()).build(); + doThrow(new IOException("boom")).doReturn(response).when(client) + .execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + config, this.inspector, loadBalancedRetryFactory); FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); - assertEquals(200, ribbonResponse.toResponse().status()); + assertThat(ribbonResponse.toResponse().status()).isEqualTo(200); verify(client, times(2)).execute(any(Request.class), any(Request.Options.class)); - assertEquals(1, backOffPolicy.getCount()); - assertEquals(1, myRetryListener.getOnError()); + assertThat(backOffPolicy.getCount()).isEqualTo(1); + assertThat(myRetryListener.getOnError()).isEqualTo(1); } @Test(expected = TerminatedRetryException.class) public void retryListenerTestNoRetry() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); SpringClientFactory clientFactory = mock(SpringClientFactory.class); IClientConfig config = mock(IClientConfig.class); doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); - doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); - doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); - doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout)); - doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout)); - doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq("")); + doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), + anyInt()); + doReturn(true).when(config) + .get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); + doReturn(this.defaultConnectTimeout).when(config) + .get(eq(CommonClientConfigKey.ConnectTimeout)); + doReturn(this.defaultReadTimeout).when(config) + .get(eq(CommonClientConfigKey.ReadTimeout)); + doReturn("").when(config).getPropertyAsString( + eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); doReturn(config).when(clientFactory).getClientConfig(eq("default")); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); MyBackOffPolicy backOffPolicy = new MyBackOffPolicy(); MyRetryListenerNotRetry myRetryListenerNotRetry = new MyRetryListenerNotRetry(); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){ + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory) { @Override public RetryListener[] createRetryListeners(String service) { - return new RetryListener[]{myRetryListenerNotRetry}; + return new RetryListener[] { myRetryListenerNotRetry }; } @Override @@ -452,26 +500,35 @@ public class RetryableFeignLoadBalancerTests { Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener")); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory); + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://listener")); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + config, this.inspector, loadBalancedRetryFactory); FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); } @Test public void retryWithDefaultConstructorTest() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); SpringClientFactory clientFactory = mock(SpringClientFactory.class); IClientConfig config = mock(IClientConfig.class); doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); - doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); - doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); - doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout)); - doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout)); - doReturn("").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES),eq("")); + doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), + anyInt()); + doReturn(true).when(config) + .get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); + doReturn(this.defaultConnectTimeout).when(config) + .get(eq(CommonClientConfigKey.ConnectTimeout)); + doReturn(this.defaultReadTimeout).when(config) + .get(eq(CommonClientConfigKey.ReadTimeout)); + doReturn("").when(config).getPropertyAsString( + eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); doReturn(config).when(clientFactory).getClientConfig(eq("default")); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); MyBackOffPolicy backOffPolicy = new MyBackOffPolicy(); - RibbonLoadBalancedRetryFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryFactory(clientFactory){ + RibbonLoadBalancedRetryFactory loadBalancedRetryPolicyFactory = new RibbonLoadBalancedRetryFactory( + clientFactory) { @Override public BackOffPolicy createBackOffPolicy(String service) { return backOffPolicy; @@ -480,36 +537,43 @@ public class RetryableFeignLoadBalancerTests { Request feignRequest = Request.create(GET, "http://listener", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://listener")); - Response response = Response.builder() - .request(feignRequest) - .status(200) - .headers(new HashMap<>()) - .build(); - doThrow(new IOException("boom")).doReturn(response).when(client).execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryPolicyFactory); + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://listener")); + Response response = Response.builder().request(feignRequest).status(200) + .headers(new HashMap<>()).build(); + doThrow(new IOException("boom")).doReturn(response).when(client) + .execute(any(Request.class), any(Request.Options.class)); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + config, this.inspector, loadBalancedRetryPolicyFactory); FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); - assertEquals(200, ribbonResponse.toResponse().status()); + assertThat(ribbonResponse.toResponse().status()).isEqualTo(200); verify(client, times(2)).execute(any(Request.class), any(Request.Options.class)); - assertEquals(1, backOffPolicy.getCount()); + assertThat(backOffPolicy.getCount()).isEqualTo(1); } @Test public void executeRetryFail() throws Exception { - RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(lb, config); + RibbonLoadBalancerContext lbContext = new RibbonLoadBalancerContext(this.lb, + this.config); lbContext.setRetryHandler(new DefaultLoadBalancerRetryHandler(1, 0, true)); SpringClientFactory clientFactory = mock(SpringClientFactory.class); IClientConfig config = mock(IClientConfig.class); doReturn(1).when(config).get(eq(CommonClientConfigKey.MaxAutoRetries), anyInt()); - doReturn(0).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), anyInt()); - doReturn(true).when(config).get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); - doReturn(defaultConnectTimeout).when(config).get(eq(CommonClientConfigKey.ConnectTimeout)); - doReturn(defaultReadTimeout).when(config).get(eq(CommonClientConfigKey.ReadTimeout)); - doReturn("404").when(config).getPropertyAsString(eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); + doReturn(0).when(config).get(eq(CommonClientConfigKey.MaxAutoRetriesNextServer), + anyInt()); + doReturn(true).when(config) + .get(eq(CommonClientConfigKey.OkToRetryOnAllOperations), eq(false)); + doReturn(this.defaultConnectTimeout).when(config) + .get(eq(CommonClientConfigKey.ConnectTimeout)); + doReturn(this.defaultReadTimeout).when(config) + .get(eq(CommonClientConfigKey.ReadTimeout)); + doReturn("404").when(config).getPropertyAsString( + eq(RibbonLoadBalancedRetryPolicy.RETRYABLE_STATUS_CODES), eq("")); doReturn(config).when(clientFactory).getClientConfig(eq("default")); doReturn(lbContext).when(clientFactory).getLoadBalancerContext(any(String.class)); MyBackOffPolicy backOffPolicy = new MyBackOffPolicy(); - RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory(clientFactory){ + RibbonLoadBalancedRetryFactory loadBalancedRetryFactory = new RibbonLoadBalancedRetryFactory( + clientFactory) { @Override public BackOffPolicy createBackOffPolicy(String service) { return backOffPolicy; @@ -518,12 +582,11 @@ public class RetryableFeignLoadBalancerTests { Request feignRequest = Request.create(GET, "http://foo", new HashMap<>(), new byte[] {}, UTF_8); Client client = mock(Client.class); - FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest(client, feignRequest, new URI("http://foo")); - Response fourOFourResponse = Response.builder() - .request(feignRequest) - .status(404) - .headers(new HashMap<>()) - .body(new Response.Body() { //set content into response + FeignLoadBalancer.RibbonRequest request = new FeignLoadBalancer.RibbonRequest( + client, feignRequest, new URI("http://foo")); + Response fourOFourResponse = Response.builder().request(feignRequest).status(404) + .headers(new HashMap<>()).body(new Response.Body() { // set content into + // response @Override public Integer length() { return "test".getBytes().length; @@ -553,15 +616,17 @@ public class RetryableFeignLoadBalancerTests { public void close() throws IOException { } }).build(); - doReturn(fourOFourResponse).when(client).execute(any(Request.class), any(Request.Options.class)); - RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(lb, config, inspector, loadBalancedRetryFactory); + doReturn(fourOFourResponse).when(client).execute(any(Request.class), + any(Request.Options.class)); + RetryableFeignLoadBalancer feignLb = new RetryableFeignLoadBalancer(this.lb, + config, this.inspector, loadBalancedRetryFactory); FeignLoadBalancer.RibbonResponse ribbonResponse = feignLb.execute(request, null); verify(client, times(2)).execute(any(Request.class), any(Request.Options.class)); - assertEquals(1, backOffPolicy.getCount()); + assertThat(backOffPolicy.getCount()).isEqualTo(1); InputStream inputStream = ribbonResponse.toResponse().body().asInputStream(); byte[] buf = new byte[100]; int read = inputStream.read(buf); - Assert.assertThat(new String(buf, 0, read), is("test")); + assertThat(new String(buf, 0, read)).isEqualTo("test"); } class MyBackOffPolicy implements BackOffPolicy { @@ -574,12 +639,13 @@ public class RetryableFeignLoadBalancerTests { } @Override - public void backOff(BackOffContext backOffContext) throws BackOffInterruptedException { - count++; + public void backOff(BackOffContext backOffContext) + throws BackOffInterruptedException { + this.count++; } public int getCount() { - return count; + return this.count; } } @@ -589,39 +655,48 @@ public class RetryableFeignLoadBalancerTests { private int onError = 0; @Override - public boolean open(RetryContext context, RetryCallback callback) { + public boolean open(RetryContext context, + RetryCallback callback) { return true; } @Override - public void close(RetryContext context, RetryCallback callback, Throwable throwable) { + public void close(RetryContext context, + RetryCallback callback, Throwable throwable) { } @Override - public void onError(RetryContext context, RetryCallback callback, Throwable throwable) { - onError++; + public void onError(RetryContext context, + RetryCallback callback, Throwable throwable) { + this.onError++; } public int getOnError() { - return onError; + return this.onError; } + } class MyRetryListenerNotRetry implements RetryListener { @Override - public boolean open(RetryContext context, RetryCallback callback) { + public boolean open(RetryContext context, + RetryCallback callback) { return false; } @Override - public void close(RetryContext context, RetryCallback callback, Throwable throwable) { + public void close(RetryContext context, + RetryCallback callback, Throwable throwable) { } @Override - public void onError(RetryContext context, RetryCallback callback, Throwable throwable) {} + public void onError(RetryContext context, + RetryCallback callback, Throwable throwable) { + } + } -} \ No newline at end of file +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/RibbonResponseStatusCodeExceptionTest.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/RibbonResponseStatusCodeExceptionTest.java index 3b8469d2..649cfb5d 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/RibbonResponseStatusCodeExceptionTest.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/ribbon/RibbonResponseStatusCodeExceptionTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + package org.springframework.cloud.openfeign.ribbon; import java.io.ByteArrayInputStream; @@ -33,7 +34,7 @@ import org.mockito.junit.MockitoJUnitRunner; import org.springframework.util.StreamUtils; import static feign.Request.HttpMethod.GET; -import static org.junit.Assert.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Ryan Baxter @@ -48,15 +49,19 @@ public class RibbonResponseStatusCodeExceptionTest { fooValues.add("bar"); headers.put("foo", fooValues); Request request = Request.create(GET, "http://service.com", - new HashMap>(), new byte[]{}, Charset.defaultCharset()); + new HashMap>(), new byte[] {}, + Charset.defaultCharset()); byte[] body = "foo".getBytes(); ByteArrayInputStream is = new ByteArrayInputStream(body); - Response response = Response.builder().status(200).reason("Success").request(request).body(is, body.length).headers(headers).build(); - RibbonResponseStatusCodeException ex = new RibbonResponseStatusCodeException("service", response, body, - new URI(request.url())); - assertEquals(200, ex.getResponse().status()); - assertEquals(request, ex.getResponse().request()); - assertEquals("Success", ex.getResponse().reason()); - assertEquals("foo", StreamUtils.copyToString(ex.getResponse().body().asInputStream(), Charset.defaultCharset())); + Response response = Response.builder().status(200).reason("Success") + .request(request).body(is, body.length).headers(headers).build(); + RibbonResponseStatusCodeException ex = new RibbonResponseStatusCodeException( + "service", response, body, new URI(request.url())); + assertThat(ex.getResponse().status()).isEqualTo(200); + assertThat(ex.getResponse().request()).isEqualTo(request); + assertThat(ex.getResponse().reason()).isEqualTo("Success"); + assertThat(StreamUtils.copyToString(ex.getResponse().body().asInputStream(), + Charset.defaultCharset())).isEqualTo("foo"); } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java index 5b3a0c1b..8365d43d 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/FeignHttpClientPropertiesTests.java @@ -1,19 +1,17 @@ /* + * Copyright 2013-2019 the original author or authors. * - * * Copyright 2013-2016 the original author or authors. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ package org.springframework.cloud.openfeign.support; @@ -21,6 +19,7 @@ package org.springframework.cloud.openfeign.support; import org.junit.After; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.boot.test.util.TestPropertyValues; @@ -30,9 +29,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.context.junit4.SpringRunner; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Ryan Baxter @@ -53,12 +50,18 @@ public class FeignHttpClientPropertiesTests { @Test public void testDefaults() { setupContext(); - assertEquals(FeignHttpClientProperties.DEFAULT_CONNECTION_TIMEOUT, getProperties().getConnectionTimeout()); - assertEquals(FeignHttpClientProperties.DEFAULT_MAX_CONNECTIONS, getProperties().getMaxConnections()); - assertEquals(FeignHttpClientProperties.DEFAULT_MAX_CONNECTIONS_PER_ROUTE, getProperties().getMaxConnectionsPerRoute()); - assertEquals(FeignHttpClientProperties.DEFAULT_TIME_TO_LIVE, getProperties().getTimeToLive()); - assertEquals(FeignHttpClientProperties.DEFAULT_DISABLE_SSL_VALIDATION, getProperties().isDisableSslValidation()); - assertEquals(FeignHttpClientProperties.DEFAULT_FOLLOW_REDIRECTS, getProperties().isFollowRedirects()); + assertThat(getProperties().getConnectionTimeout()) + .isEqualTo(FeignHttpClientProperties.DEFAULT_CONNECTION_TIMEOUT); + assertThat(getProperties().getMaxConnections()) + .isEqualTo(FeignHttpClientProperties.DEFAULT_MAX_CONNECTIONS); + assertThat(getProperties().getMaxConnectionsPerRoute()) + .isEqualTo(FeignHttpClientProperties.DEFAULT_MAX_CONNECTIONS_PER_ROUTE); + assertThat(getProperties().getTimeToLive()) + .isEqualTo(FeignHttpClientProperties.DEFAULT_TIME_TO_LIVE); + assertThat(getProperties().isDisableSslValidation()) + .isEqualTo(FeignHttpClientProperties.DEFAULT_DISABLE_SSL_VALIDATION); + assertThat(getProperties().isFollowRedirects()) + .isEqualTo(FeignHttpClientProperties.DEFAULT_FOLLOW_REDIRECTS); } @Test @@ -70,16 +73,17 @@ public class FeignHttpClientPropertiesTests { "feign.httpclient.disableSslValidation=true", "feign.httpclient.followRedirects=false").applyTo(this.context); setupContext(); - assertEquals(2, getProperties().getMaxConnections()); - assertEquals(2, getProperties().getConnectionTimeout()); - assertEquals(2, getProperties().getMaxConnectionsPerRoute()); - assertEquals(2L, getProperties().getTimeToLive()); - assertTrue(getProperties().isDisableSslValidation()); - assertFalse(getProperties().isFollowRedirects()); + assertThat(getProperties().getMaxConnections()).isEqualTo(2); + assertThat(getProperties().getConnectionTimeout()).isEqualTo(2); + assertThat(getProperties().getMaxConnectionsPerRoute()).isEqualTo(2); + assertThat(getProperties().getTimeToLive()).isEqualTo(2L); + assertThat(getProperties().isDisableSslValidation()).isTrue(); + assertThat(getProperties().isFollowRedirects()).isFalse(); } private void setupContext() { - this.context.register(PropertyPlaceholderAutoConfiguration.class, TestConfiguration.class); + this.context.register(PropertyPlaceholderAutoConfiguration.class, + TestConfiguration.class); this.context.refresh(); } @@ -90,9 +94,12 @@ public class FeignHttpClientPropertiesTests { @Configuration @EnableConfigurationProperties protected static class TestConfiguration { + @Bean FeignHttpClientProperties zuulProperties() { - return new FeignHttpClientProperties() ; + return new FeignHttpClientProperties(); } + } -} \ No newline at end of file + +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java index d6477427..bd970035 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringEncoderTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.support; @@ -50,12 +49,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.multipart.MultipartFile; -import static org.hamcrest.Matchers.containsString; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.hamcrest.Matchers.nullValue; -import static org.junit.Assert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.springframework.http.HttpHeaders.ACCEPT; import static org.springframework.http.HttpHeaders.CONTENT_LENGTH; import static org.springframework.http.HttpHeaders.CONTENT_TYPE; @@ -82,110 +76,93 @@ public class SpringEncoderTests { @Test public void testCustomHttpMessageConverter() { SpringEncoder encoder = this.context.getInstance("foo", SpringEncoder.class); - assertThat(encoder, is(notNullValue())); + assertThat(encoder).isNotNull(); RequestTemplate request = new RequestTemplate(); encoder.encode("hi", MyType.class, request); Collection contentTypeHeader = request.headers().get("Content-Type"); - assertThat("missing content type header", contentTypeHeader, is(notNullValue())); - assertThat("missing content type header", contentTypeHeader.isEmpty(), is(false)); + assertThat(contentTypeHeader).as("missing content type header").isNotNull(); + assertThat(contentTypeHeader.isEmpty()).as("missing content type header") + .isFalse(); String header = contentTypeHeader.iterator().next(); - assertThat("content type header is wrong", header, is("application/mytype")); - - assertThat("request charset is null", request.requestCharset(), is(notNullValue())); - assertThat("request charset is wrong", request.requestCharset(), is(Charset.forName("UTF-8"))); + assertThat(header).as("content type header is wrong") + .isEqualTo("application/mytype"); + + assertThat(request.requestCharset()).as("request charset is null").isNotNull(); + assertThat(request.requestCharset()).as("request charset is wrong") + .isEqualTo(Charset.forName("UTF-8")); } @Test public void testBinaryData() { SpringEncoder encoder = this.context.getInstance("foo", SpringEncoder.class); - assertThat(encoder, is(notNullValue())); + assertThat(encoder).isNotNull(); RequestTemplate request = new RequestTemplate(); encoder.encode("hi".getBytes(), null, request); - assertThat("Request Content-Type is not octet-stream", - ((List) request.headers().get(CONTENT_TYPE)).get(0), - equalTo(APPLICATION_OCTET_STREAM_VALUE)); + assertThat(((List) request.headers().get(CONTENT_TYPE)).get(0)) + .as("Request Content-Type is not octet-stream") + .isEqualTo(APPLICATION_OCTET_STREAM_VALUE); } @Test(expected = EncodeException.class) public void testMultipartFile1() { SpringEncoder encoder = this.context.getInstance("foo", SpringEncoder.class); - assertThat(encoder, is(notNullValue())); + assertThat(encoder).isNotNull(); RequestTemplate request = new RequestTemplate(); - MultipartFile multipartFile = new MockMultipartFile("test_multipart_file", "hi".getBytes()); + MultipartFile multipartFile = new MockMultipartFile("test_multipart_file", + "hi".getBytes()); encoder.encode(multipartFile, MultipartFile.class, request); - assertThat("request charset is not null", request.requestCharset(), is(nullValue())); + assertThat(request.requestCharset()).as("request charset is not null").isNull(); } // gh-105, gh-107 @Test public void testMultipartFile2() { SpringEncoder encoder = this.context.getInstance("foo", SpringEncoder.class); - assertThat(encoder, is(notNullValue())); + assertThat(encoder).isNotNull(); RequestTemplate request = new RequestTemplate(); request.header(ACCEPT, MediaType.MULTIPART_FORM_DATA_VALUE); request.header(CONTENT_TYPE, MediaType.MULTIPART_FORM_DATA_VALUE); - MultipartFile multipartFile = new MockMultipartFile("test_multipart_file", "hi".getBytes()); + MultipartFile multipartFile = new MockMultipartFile("test_multipart_file", + "hi".getBytes()); encoder.encode(multipartFile, MultipartFile.class, request); - assertThat("Request Content-Type is not multipart/form-data", - (String) ((List) request.headers().get(CONTENT_TYPE)).get(0), - containsString("multipart/form-data; charset=UTF-8; boundary=")); - assertThat("There is more than one Content-Type request header", - request.headers().get(CONTENT_TYPE).size(), equalTo(1)); - assertThat("Request Accept header is not multipart/form-data", - ((List) request.headers().get(ACCEPT)).get(0), - equalTo(MULTIPART_FORM_DATA_VALUE)); - assertThat("Request Content-Length is not equal to 186", - ((List) request.headers().get(CONTENT_LENGTH)).get(0), - equalTo("186")); - assertThat("Body content cannot be decoded", - new String(request.requestBody().asBytes()), - containsString("hi")); + assertThat((String) ((List) request.headers().get(CONTENT_TYPE)).get(0)) + .as("Request Content-Type is not multipart/form-data") + .contains("multipart/form-data; charset=UTF-8; boundary="); + assertThat(request.headers().get(CONTENT_TYPE).size()) + .as("There is more than one Content-Type request header").isEqualTo(1); + assertThat(((List) request.headers().get(ACCEPT)).get(0)) + .as("Request Accept header is not multipart/form-data") + .isEqualTo(MULTIPART_FORM_DATA_VALUE); + assertThat(((List) request.headers().get(CONTENT_LENGTH)).get(0)) + .as("Request Content-Length is not equal to 186").isEqualTo("186"); + assertThat(new String(request.requestBody().asBytes())) + .as("Body content cannot be decoded").contains("hi"); } - - class MediaTypeMatcher implements ArgumentMatcher { - private MediaType mediaType; + protected interface TestClient { - public MediaTypeMatcher(String type, String subtype) { - this.mediaType = new MediaType(type, subtype); - } - - @Override - public boolean matches(MediaType argument) { - return this.mediaType.equals(argument); - } - - @Override - public String toString() { - final StringBuffer sb = new StringBuffer("MediaTypeMatcher{"); - sb.append("mediaType=").append(this.mediaType); - sb.append('}'); - return sb.toString(); - } } protected static class MyType { + private String value; public String getValue() { - return value; + return this.value; } public void setValue(String value) { this.value = value; } - } - - protected interface TestClient { } @@ -202,7 +179,7 @@ public class SpringEncoderTests { private static class MyHttpMessageConverter extends AbstractGenericHttpMessageConverter { - public MyHttpMessageConverter() { + MyHttpMessageConverter() { super(new MediaType("application", "mytype")); } @@ -240,7 +217,32 @@ public class SpringEncoderTests { throws IOException, HttpMessageNotReadableException { return null; } + } + + } + + class MediaTypeMatcher implements ArgumentMatcher { + + private MediaType mediaType; + + MediaTypeMatcher(String type, String subtype) { + this.mediaType = new MediaType(type, subtype); + } + + @Override + public boolean matches(MediaType argument) { + return this.mediaType.equals(argument); + } + + @Override + public String toString() { + final StringBuffer sb = new StringBuffer("MediaTypeMatcher{"); + sb.append("mediaType=").append(this.mediaType); + sb.append('}'); + return sb.toString(); + } + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java index 663c9f08..3838e6f0 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/support/SpringMvcContractTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,10 +53,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.ANY; +import static com.fasterxml.jackson.annotation.JsonAutoDetect.Visibility.NONE; import static java.nio.charset.StandardCharsets.UTF_8; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assume.assumeTrue; import static org.springframework.web.util.UriUtils.encode; @@ -66,6 +66,7 @@ import static org.springframework.web.util.UriUtils.encode; * @author Aram Peres */ public class SpringMvcContractTests { + private static final Class EXECUTABLE_TYPE; static { @@ -81,364 +82,11 @@ public class SpringMvcContractTests { private SpringMvcContract contract; - @Before - public void setup() { - FormattingConversionServiceFactoryBean conversionServiceFactoryBean - = new FormattingConversionServiceFactoryBean(); - conversionServiceFactoryBean.afterPropertiesSet(); - ConversionService conversionService = conversionServiceFactoryBean.getObject(); - - this.contract = new SpringMvcContract(Collections.emptyList(), conversionService); - } - - @Test - public void testProcessAnnotationOnMethod_Simple() throws Exception { - Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest", - String.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/test/{id}", data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - } - - @Test - public void testProcessAnnotations_Simple() throws Exception { - Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest", - String.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/test/{id}", data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - - assertEquals("id", data.indexToName().get(0).iterator().next()); - } - - @Test - public void testProcessAnnotations_SimpleGetMapping() throws Exception { - Method method = TestTemplate_Simple.class.getDeclaredMethod("getMappingTest", - String.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/test/{id}", data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - - assertEquals("id", data.indexToName().get(0).iterator().next()); - } - - @Test - public void testProcessAnnotations_Class_AnnotationsGetSpecificTest() - throws Exception { - Method method = TestTemplate_Class_Annotations.class - .getDeclaredMethod("getSpecificTest", String.class, String.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/prepend/{classId}/test/{testId}", data.template().url()); - assertEquals("GET", data.template().method()); - - assertEquals("classId", data.indexToName().get(0).iterator().next()); - assertEquals("testId", data.indexToName().get(1).iterator().next()); - } - - @Test - public void testProcessAnnotations_Class_AnnotationsGetAllTests() throws Exception { - Method method = TestTemplate_Class_Annotations.class - .getDeclaredMethod("getAllTests", String.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/prepend/{classId}", data.template().url()); - assertEquals("GET", data.template().method()); - - assertEquals("classId", data.indexToName().get(0).iterator().next()); - } - - @Test - public void testProcessAnnotations_ExtendedInterface() throws Exception { - Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests", - String.class); - MethodMetadata extendedData = this.contract.parseAndValidateMetadata( - extendedMethod.getDeclaringClass(), extendedMethod); - - Method method = TestTemplate_Class_Annotations.class - .getDeclaredMethod("getAllTests", String.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals(extendedData.template().url(), data.template().url()); - assertEquals(extendedData.template().method(), data.template().method()); - - assertEquals(data.indexToName().get(0).iterator().next(), - data.indexToName().get(0).iterator().next()); - } - - @Test - public void testProcessAnnotations_SimplePost() throws Exception { - Method method = TestTemplate_Simple.class.getDeclaredMethod("postTest", - TestObject.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/", data.template().url()); - assertEquals("POST", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - - } - - @Test - public void testProcessAnnotations_SimplePostMapping() throws Exception { - Method method = TestTemplate_Simple.class.getDeclaredMethod("postMappingTest", - TestObject.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/", data.template().url()); - assertEquals("POST", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - - } - - @Test - public void testProcessAnnotationsOnMethod_Advanced() throws Exception { - Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", - String.class, String.class, Integer.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/advanced/test/{id}?amount=" + encode("{amount}", UTF_8), - data.template().url()); - assertEquals("PUT", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - } - - @Test - public void testProcessAnnotationsOnMethod_Advanced_UnknownAnnotation() - throws Exception { - Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", - String.class, String.class, Integer.class); - this.contract.parseAndValidateMetadata(method.getDeclaringClass(), method); - - // Don't throw an exception and this passes - } - - @Test - public void testProcessAnnotations_Advanced() throws Exception { - Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", - String.class, String.class, Integer.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/advanced/test/{id}?amount=" + encode("{amount}", UTF_8), - data.template().url()); - assertEquals("PUT", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - - assertEquals("Authorization", data.indexToName().get(0).iterator().next()); - assertEquals("id", data.indexToName().get(1).iterator().next()); - assertEquals("amount", data.indexToName().get(2).iterator().next()); - assertNotNull(data.indexToExpander().get(2)); - - assertEquals("{Authorization}", - data.template().headers().get("Authorization").iterator().next()); - assertEquals("{amount}", - data.template().queries().get("amount").iterator().next()); - } - - @Test - public void testProcessAnnotations_Aliased() throws Exception { - Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest2", - String.class, Integer.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/advanced/test2?amount=" + encode("{amount}", UTF_8), - data.template().url()); - assertEquals("PUT", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - - assertEquals("Authorization", data.indexToName().get(0).iterator().next()); - assertEquals("amount", data.indexToName().get(1).iterator().next()); - - assertEquals("{Authorization}", - data.template().headers().get("Authorization").iterator().next()); - assertEquals("{amount}", - data.template().queries().get("amount").iterator().next()); - } - - @Test - public void testProcessAnnotations_DateTimeFormatParam() throws Exception { - Method method = TestTemplate_DateTimeFormatParameter.class.getDeclaredMethod( - "getTest", LocalDateTime.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - Param.Expander expander = data.indexToExpander().get(0); - assertNotNull(expander); - - LocalDateTime input = LocalDateTime.of(2001, 10, 12, 23, 56, 3); - - DateTimeFormatter formatter = DateTimeFormatter.ofPattern( - TestTemplate_DateTimeFormatParameter.CUSTOM_PATTERN); - - String expected = formatter.format(input); - - assertEquals(expected, expander.expand(input)); - } - - @Test - public void testProcessAnnotations_NumberFormatParam() throws Exception { - Method method = TestTemplate_NumberFormatParameter.class.getDeclaredMethod( - "getTest", BigDecimal.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - Param.Expander expander = data.indexToExpander().get(0); - assertNotNull(expander); - - NumberStyleFormatter formatter = new NumberStyleFormatter( - TestTemplate_NumberFormatParameter.CUSTOM_PATTERN); - - BigDecimal input = BigDecimal.valueOf(1220.345); - - String expected = formatter.print(input, Locale.getDefault()); - String actual = expander.expand(input); - - assertEquals(expected, actual); - } - - @Test - public void testProcessAnnotations_Advanced2() throws Exception { - Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest"); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/advanced", data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - } - - @Test - public void testProcessAnnotations_Advanced3() throws Exception { - Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest"); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/", data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - } - - @Test - public void testProcessAnnotations_ListParams() throws Exception { - Method method = TestTemplate_ListParams.class.getDeclaredMethod("getTest", - List.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/test?id=" + encode("{id}", UTF_8), - data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals("[{id}]", data.template().queries().get("id").toString()); - assertNotNull(data.indexToExpander().get(0)); - } - - @Test - public void testProcessAnnotations_ListParamsWithoutName() throws Exception { - Method method = TestTemplate_ListParamsWithoutName.class.getDeclaredMethod("getTest", - List.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/test?id=" + encode("{id}", UTF_8), data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals("[{id}]", data.template().queries().get("id").toString()); - assertNotNull(data.indexToExpander().get(0)); - } - - @Test - public void testProcessAnnotations_MapParams() throws Exception { - Method method = TestTemplate_MapParams.class.getDeclaredMethod("getTest", - Map.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/test", data.template().url()); - assertEquals("GET", data.template().method()); - assertNotNull(data.queryMapIndex()); - assertEquals(0, data.queryMapIndex().intValue()); - } - - @Test - public void testProcessHeaders() throws Exception { - Method method = TestTemplate_Headers.class.getDeclaredMethod("getTest", - String.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/test/{id}", data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals("bar", data.template().headers().get("X-Foo").iterator().next()); - } - - @Test - public void testProcessHeadersWithoutValues() throws Exception { - Method method = TestTemplate_HeadersWithoutValues.class.getDeclaredMethod("getTest", - String.class); - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/test/{id}", data.template().url()); - assertEquals("GET", data.template().method()); - assertTrue(data.template().headers().isEmpty()); - } - - @Test - public void testProcessAnnotations_Fallback() throws Exception { - Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTestFallback", - String.class, String.class, Integer.class); - - assumeTrue("does not have java 8 parameter names", hasJava8ParameterNames(method)); - - MethodMetadata data = this.contract - .parseAndValidateMetadata(method.getDeclaringClass(), method); - - assertEquals("/advanced/testfallback/{id}?amount=" + encode("{amount}", UTF_8), data - .template().url()); - assertEquals("PUT", data.template().method()); - assertEquals(MediaType.APPLICATION_JSON_VALUE, - data.template().headers().get("Accept").iterator().next()); - - assertEquals("Authorization", data.indexToName().get(0).iterator().next()); - assertEquals("id", data.indexToName().get(1).iterator().next()); - assertEquals("amount", data.indexToName().get(2).iterator().next()); - - assertEquals("{Authorization}", - data.template().headers().get("Authorization").iterator().next()); - assertEquals("{amount}", - data.template().queries().get("amount").iterator().next()); - } - /** * For abstract (e.g. interface) methods, only Java 8 Parameter names (compiler arg * -parameters) can supply parameter names; bytecode-based strategies use local * variable declarations, of which there are none for abstract methods. - * @param m + * @param m method * @return whether a parameter name was found * @throws IllegalArgumentException if method has no parameters */ @@ -461,6 +109,362 @@ public class SpringMvcContractTests { return false; } + @Before + public void setup() { + FormattingConversionServiceFactoryBean conversionServiceFactoryBean = new FormattingConversionServiceFactoryBean(); + conversionServiceFactoryBean.afterPropertiesSet(); + ConversionService conversionService = conversionServiceFactoryBean.getObject(); + + this.contract = new SpringMvcContract(Collections.emptyList(), conversionService); + } + + @Test + public void testProcessAnnotationOnMethod_Simple() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest", + String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test/{id}"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + } + + @Test + public void testProcessAnnotations_Simple() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest", + String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test/{id}"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + + assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("id"); + } + + @Test + public void testProcessAnnotations_SimpleGetMapping() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("getMappingTest", + String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test/{id}"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + + assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("id"); + } + + @Test + public void testProcessAnnotations_Class_AnnotationsGetSpecificTest() + throws Exception { + Method method = TestTemplate_Class_Annotations.class + .getDeclaredMethod("getSpecificTest", String.class, String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/prepend/{classId}/test/{testId}"); + assertThat(data.template().method()).isEqualTo("GET"); + + assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId"); + assertThat(data.indexToName().get(1).iterator().next()).isEqualTo("testId"); + } + + @Test + public void testProcessAnnotations_Class_AnnotationsGetAllTests() throws Exception { + Method method = TestTemplate_Class_Annotations.class + .getDeclaredMethod("getAllTests", String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/prepend/{classId}"); + assertThat(data.template().method()).isEqualTo("GET"); + + assertThat(data.indexToName().get(0).iterator().next()).isEqualTo("classId"); + } + + @Test + public void testProcessAnnotations_ExtendedInterface() throws Exception { + Method extendedMethod = TestTemplate_Extended.class.getMethod("getAllTests", + String.class); + MethodMetadata extendedData = this.contract.parseAndValidateMetadata( + extendedMethod.getDeclaringClass(), extendedMethod); + + Method method = TestTemplate_Class_Annotations.class + .getDeclaredMethod("getAllTests", String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo(extendedData.template().url()); + assertThat(data.template().method()).isEqualTo(extendedData.template().method()); + + assertThat(data.indexToName().get(0).iterator().next()) + .isEqualTo(data.indexToName().get(0).iterator().next()); + } + + @Test + public void testProcessAnnotations_SimplePost() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("postTest", + TestObject.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/"); + assertThat(data.template().method()).isEqualTo("POST"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + + } + + @Test + public void testProcessAnnotations_SimplePostMapping() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("postMappingTest", + TestObject.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/"); + assertThat(data.template().method()).isEqualTo("POST"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + + } + + @Test + public void testProcessAnnotationsOnMethod_Advanced() throws Exception { + Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", + String.class, String.class, Integer.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()) + .isEqualTo("/advanced/test/{id}?amount=" + encode("{amount}", UTF_8)); + assertThat(data.template().method()).isEqualTo("PUT"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + } + + @Test + public void testProcessAnnotationsOnMethod_Advanced_UnknownAnnotation() + throws Exception { + Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", + String.class, String.class, Integer.class); + this.contract.parseAndValidateMetadata(method.getDeclaringClass(), method); + + // Don't throw an exception and this passes + } + + @Test + public void testProcessAnnotations_Advanced() throws Exception { + Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest", + String.class, String.class, Integer.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()) + .isEqualTo("/advanced/test/{id}?amount=" + encode("{amount}", UTF_8)); + assertThat(data.template().method()).isEqualTo("PUT"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + + assertThat(data.indexToName().get(0).iterator().next()) + .isEqualTo("Authorization"); + assertThat(data.indexToName().get(1).iterator().next()).isEqualTo("id"); + assertThat(data.indexToName().get(2).iterator().next()).isEqualTo("amount"); + assertThat(data.indexToExpander().get(2)).isNotNull(); + + assertThat(data.template().headers().get("Authorization").iterator().next()) + .isEqualTo("{Authorization}"); + assertThat(data.template().queries().get("amount").iterator().next()) + .isEqualTo("{amount}"); + } + + @Test + public void testProcessAnnotations_Aliased() throws Exception { + Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest2", + String.class, Integer.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()) + .isEqualTo("/advanced/test2?amount=" + encode("{amount}", UTF_8)); + assertThat(data.template().method()).isEqualTo("PUT"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + + assertThat(data.indexToName().get(0).iterator().next()) + .isEqualTo("Authorization"); + assertThat(data.indexToName().get(1).iterator().next()).isEqualTo("amount"); + + assertThat(data.template().headers().get("Authorization").iterator().next()) + .isEqualTo("{Authorization}"); + assertThat(data.template().queries().get("amount").iterator().next()) + .isEqualTo("{amount}"); + } + + @Test + public void testProcessAnnotations_DateTimeFormatParam() throws Exception { + Method method = TestTemplate_DateTimeFormatParameter.class + .getDeclaredMethod("getTest", LocalDateTime.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + Param.Expander expander = data.indexToExpander().get(0); + assertThat(expander).isNotNull(); + + LocalDateTime input = LocalDateTime.of(2001, 10, 12, 23, 56, 3); + + DateTimeFormatter formatter = DateTimeFormatter + .ofPattern(TestTemplate_DateTimeFormatParameter.CUSTOM_PATTERN); + + String expected = formatter.format(input); + + assertThat(expander.expand(input)).isEqualTo(expected); + } + + @Test + public void testProcessAnnotations_NumberFormatParam() throws Exception { + Method method = TestTemplate_NumberFormatParameter.class + .getDeclaredMethod("getTest", BigDecimal.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + Param.Expander expander = data.indexToExpander().get(0); + assertThat(expander).isNotNull(); + + NumberStyleFormatter formatter = new NumberStyleFormatter( + TestTemplate_NumberFormatParameter.CUSTOM_PATTERN); + + BigDecimal input = BigDecimal.valueOf(1220.345); + + String expected = formatter.print(input, Locale.getDefault()); + String actual = expander.expand(input); + + assertThat(actual).isEqualTo(expected); + } + + @Test + public void testProcessAnnotations_Advanced2() throws Exception { + Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTest"); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/advanced"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + } + + @Test + public void testProcessAnnotations_Advanced3() throws Exception { + Method method = TestTemplate_Simple.class.getDeclaredMethod("getTest"); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + } + + @Test + public void testProcessAnnotations_ListParams() throws Exception { + Method method = TestTemplate_ListParams.class.getDeclaredMethod("getTest", + List.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test?id=" + encode("{id}", UTF_8)); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().queries().get("id").toString()).isEqualTo("[{id}]"); + assertThat(data.indexToExpander().get(0)).isNotNull(); + } + + @Test + public void testProcessAnnotations_ListParamsWithoutName() throws Exception { + Method method = TestTemplate_ListParamsWithoutName.class + .getDeclaredMethod("getTest", List.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test?id=" + encode("{id}", UTF_8)); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().queries().get("id").toString()).isEqualTo("[{id}]"); + assertThat(data.indexToExpander().get(0)).isNotNull(); + } + + @Test + public void testProcessAnnotations_MapParams() throws Exception { + Method method = TestTemplate_MapParams.class.getDeclaredMethod("getTest", + Map.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.queryMapIndex()).isNotNull(); + assertThat(data.queryMapIndex().intValue()).isEqualTo(0); + } + + @Test + public void testProcessHeaders() throws Exception { + Method method = TestTemplate_Headers.class.getDeclaredMethod("getTest", + String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test/{id}"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().get("X-Foo").iterator().next()) + .isEqualTo("bar"); + } + + @Test + public void testProcessHeadersWithoutValues() throws Exception { + Method method = TestTemplate_HeadersWithoutValues.class + .getDeclaredMethod("getTest", String.class); + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo("/test/{id}"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.template().headers().isEmpty()).isTrue(); + } + + @Test + public void testProcessAnnotations_Fallback() throws Exception { + Method method = TestTemplate_Advanced.class.getDeclaredMethod("getTestFallback", + String.class, String.class, Integer.class); + + assumeTrue("does not have java 8 parameter names", + hasJava8ParameterNames(method)); + + MethodMetadata data = this.contract + .parseAndValidateMetadata(method.getDeclaringClass(), method); + + assertThat(data.template().url()).isEqualTo( + "/advanced/testfallback/{id}?amount=" + encode("{amount}", UTF_8)); + assertThat(data.template().method()).isEqualTo("PUT"); + assertThat(data.template().headers().get("Accept").iterator().next()) + .isEqualTo(MediaType.APPLICATION_JSON_VALUE); + + assertThat(data.indexToName().get(0).iterator().next()) + .isEqualTo("Authorization"); + assertThat(data.indexToName().get(1).iterator().next()).isEqualTo("id"); + assertThat(data.indexToName().get(2).iterator().next()).isEqualTo("amount"); + + assertThat(data.template().headers().get("Authorization").iterator().next()) + .isEqualTo("{Authorization}"); + assertThat(data.template().queries().get("amount").iterator().next()) + .isEqualTo("{amount}"); + } + @Test public void testProcessHeaderMap() throws Exception { Method method = TestTemplate_HeaderMap.class.getDeclaredMethod("headerMap", @@ -468,11 +472,11 @@ public class SpringMvcContractTests { MethodMetadata data = this.contract .parseAndValidateMetadata(method.getDeclaringClass(), method); - assertEquals("/headerMap", data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals(0, data.headerMapIndex().intValue()); + assertThat(data.template().url()).isEqualTo("/headerMap"); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.headerMapIndex().intValue()).isEqualTo(0); Map> headers = data.template().headers(); - assertEquals("{aHeader}", headers.get("aHeader").iterator().next()); + assertThat(headers.get("aHeader").iterator().next()).isEqualTo("{aHeader}"); } @Test(expected = IllegalStateException.class) @@ -489,12 +493,12 @@ public class SpringMvcContractTests { MethodMetadata data = this.contract .parseAndValidateMetadata(method.getDeclaringClass(), method); - assertEquals("/queryMap?aParam=" + encode("{aParam}", UTF_8), - data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals(0, data.queryMapIndex().intValue()); + assertThat(data.template().url()) + .isEqualTo("/queryMap?aParam=" + encode("{aParam}", UTF_8)); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.queryMapIndex().intValue()).isEqualTo(0); Map> params = data.template().queries(); - assertEquals("{aParam}", params.get("aParam").iterator().next()); + assertThat(params.get("aParam").iterator().next()).isEqualTo("{aParam}"); } @Test @@ -504,12 +508,12 @@ public class SpringMvcContractTests { MethodMetadata data = this.contract .parseAndValidateMetadata(method.getDeclaringClass(), method); - assertEquals("/queryMapObject?aParam=" + encode("{aParam}", UTF_8), - data.template().url()); - assertEquals("GET", data.template().method()); - assertEquals(0, data.queryMapIndex().intValue()); + assertThat(data.template().url()) + .isEqualTo("/queryMapObject?aParam=" + encode("{aParam}", UTF_8)); + assertThat(data.template().method()).isEqualTo("GET"); + assertThat(data.queryMapIndex().intValue()).isEqualTo(0); Map> params = data.template().queries(); - assertEquals("{aParam}", params.get("aParam").iterator().next()); + assertThat(params.get("aParam").iterator().next()).isEqualTo("{aParam}"); } @Test(expected = IllegalStateException.class) @@ -520,6 +524,7 @@ public class SpringMvcContractTests { } public interface TestTemplate_Simple { + @RequestMapping(value = "/test/{id}", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity getTest(@PathVariable("id") String id); @@ -534,16 +539,19 @@ public class SpringMvcContractTests { @PostMapping(produces = MediaType.APPLICATION_JSON_VALUE) TestObject postMappingTest(@RequestBody TestObject object); + } @RequestMapping("/prepend/{classId}") public interface TestTemplate_Class_Annotations { + @RequestMapping(value = "/test/{testId}", method = RequestMethod.GET) TestObject getSpecificTest(@PathVariable("classId") String classId, @PathVariable("testId") String testId); @RequestMapping(method = RequestMethod.GET) TestObject getAllTests(@PathVariable("classId") String classId); + } public interface TestTemplate_Extended extends TestTemplate_Class_Annotations { @@ -551,57 +559,68 @@ public class SpringMvcContractTests { } public interface TestTemplate_Headers { + @RequestMapping(value = "/test/{id}", method = RequestMethod.GET, headers = "X-Foo=bar") ResponseEntity getTest(@PathVariable("id") String id); + } public interface TestTemplate_HeadersWithoutValues { - @RequestMapping(value = "/test/{id}", method = RequestMethod.GET, headers = { "X-Foo", "!X-Bar", "X-Baz!=fooBar" }) + + @RequestMapping(value = "/test/{id}", method = RequestMethod.GET, headers = { + "X-Foo", "!X-Bar", "X-Baz!=fooBar" }) ResponseEntity getTest(@PathVariable("id") String id); + } public interface TestTemplate_ListParams { + @RequestMapping(value = "/test", method = RequestMethod.GET) ResponseEntity getTest(@RequestParam("id") List id); + } public interface TestTemplate_ListParamsWithoutName { + @RequestMapping(value = "/test", method = RequestMethod.GET) ResponseEntity getTest(@RequestParam List id); + } public interface TestTemplate_MapParams { + @RequestMapping(value = "/test", method = RequestMethod.GET) ResponseEntity getTest(@RequestParam Map params); + } public interface TestTemplate_HeaderMap { + @RequestMapping(path = "/headerMap") - String headerMap( - @RequestHeader MultiValueMap headerMap, + String headerMap(@RequestHeader MultiValueMap headerMap, @RequestHeader(name = "aHeader") String aHeader); @RequestMapping(path = "/headerMapMoreThanOnce") String headerMapMoreThanOnce( @RequestHeader MultiValueMap headerMap1, @RequestHeader MultiValueMap headerMap2); + } public interface TestTemplate_QueryMap { + @RequestMapping(path = "/queryMap") - String queryMap( - @RequestParam MultiValueMap queryMap, + String queryMap(@RequestParam MultiValueMap queryMap, @RequestParam(name = "aParam") String aParam); @RequestMapping(path = "/queryMapMoreThanOnce") - String queryMapMoreThanOnce( - @RequestParam MultiValueMap queryMap1, + String queryMapMoreThanOnce(@RequestParam MultiValueMap queryMap1, @RequestParam MultiValueMap queryMap2); @RequestMapping(path = "/queryMapObject") - String queryMapObject( - @SpringQueryMap TestObject queryMap, + String queryMapObject(@SpringQueryMap TestObject queryMap, @RequestParam(name = "aParam") String aParam); + } @JsonAutoDetect @@ -612,7 +631,7 @@ public class SpringMvcContractTests { @RequestMapping(path = "/test/{id}", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity getTest(@RequestHeader("Authorization") String auth, @PathVariable("id") String id, @RequestParam("amount") Integer amount); - + @RequestMapping(path = "/test2", method = RequestMethod.PUT, produces = MediaType.APPLICATION_JSON_VALUE) ResponseEntity getTest2( @RequestHeader(name = "Authorization") String auth, @@ -625,6 +644,7 @@ public class SpringMvcContractTests { @RequestMapping(method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE) TestObject getTest(); + } public interface TestTemplate_DateTimeFormatParameter { @@ -632,8 +652,9 @@ public class SpringMvcContractTests { String CUSTOM_PATTERN = "dd-MM-yyyy HH:mm"; @RequestMapping(method = RequestMethod.GET) - String getTest(@RequestParam(name = "localDateTime") - @DateTimeFormat(pattern = CUSTOM_PATTERN) LocalDateTime localDateTime); + String getTest( + @RequestParam(name = "localDateTime") @DateTimeFormat(pattern = CUSTOM_PATTERN) LocalDateTime localDateTime); + } public interface TestTemplate_NumberFormatParameter { @@ -641,14 +662,16 @@ public class SpringMvcContractTests { String CUSTOM_PATTERN = "$###,###.###"; @RequestMapping(method = RequestMethod.GET) - String getTest(@RequestParam("amount") - @NumberFormat(pattern = CUSTOM_PATTERN) BigDecimal amount); + String getTest( + @RequestParam("amount") @NumberFormat(pattern = CUSTOM_PATTERN) BigDecimal amount); + } - @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, setterVisibility = JsonAutoDetect.Visibility.NONE) + @JsonAutoDetect(fieldVisibility = ANY, getterVisibility = NONE, setterVisibility = NONE) public class TestObject { public String something; + public Double number; public TestObject() { @@ -691,10 +714,11 @@ public class SpringMvcContractTests { @Override public String toString() { - return new StringBuilder("TestObject{") - .append("something='").append(something).append("', ") - .append("number=").append(number) - .append("}").toString(); + return new StringBuilder("TestObject{").append("something='") + .append(this.something).append("', ").append("number=") + .append(this.number).append("}").toString(); } + } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/ApacheHttpClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/ApacheHttpClientConfigurationTests.java index ead7604a..606bcf44 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/ApacheHttpClientConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/ApacheHttpClientConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.test; @@ -57,7 +56,6 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; @@ -67,9 +65,8 @@ import static org.mockito.Mockito.mockingDetails; * @author Ryan Baxter */ @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(properties = - {"feign.okhttp.enabled: false", - "ribbon.eureka.enabled = false"}) +@SpringBootTest(properties = { "feign.okhttp.enabled: false", + "ribbon.eureka.enabled = false" }) @DirtiesContext public class ApacheHttpClientConfigurationTests { @@ -84,49 +81,65 @@ public class ApacheHttpClientConfigurationTests { @Test public void testFactories() { - assertThat(connectionManagerFactory).isInstanceOf(ApacheHttpClientConnectionManagerFactory.class); - assertThat(connectionManagerFactory).isInstanceOf(ApacheHttpClientConfigurationTestApp.MyApacheHttpClientConnectionManagerFactory.class); - assertThat(httpClientFactory).isInstanceOf(ApacheHttpClientFactory.class); - assertThat(httpClientFactory).isInstanceOf(ApacheHttpClientConfigurationTestApp.MyApacheHttpClientFactory.class); + assertThat(this.connectionManagerFactory) + .isInstanceOf(ApacheHttpClientConnectionManagerFactory.class); + assertThat(this.connectionManagerFactory).isInstanceOf( + ApacheHttpClientConfigurationTestApp.MyApacheHttpClientConnectionManagerFactory.class); + assertThat(this.httpClientFactory).isInstanceOf(ApacheHttpClientFactory.class); + assertThat(this.httpClientFactory).isInstanceOf( + ApacheHttpClientConfigurationTestApp.MyApacheHttpClientFactory.class); } @Test public void testHttpClientWithFeign() { - Client delegate = feignClient.getDelegate(); - assertTrue(ApacheHttpClient.class.isInstance(delegate)); - ApacheHttpClient apacheHttpClient = (ApacheHttpClient)delegate; + Client delegate = this.feignClient.getDelegate(); + assertThat(ApacheHttpClient.class.isInstance(delegate)).isTrue(); + ApacheHttpClient apacheHttpClient = (ApacheHttpClient) delegate; HttpClient httpClient = getField(apacheHttpClient, "client"); MockingDetails httpClientDetails = mockingDetails(httpClient); - assertTrue(httpClientDetails.isMock()); + assertThat(httpClientDetails.isMock()).isTrue(); } protected T getField(Object target, String name) { Field field = ReflectionUtils.findField(target.getClass(), name); ReflectionUtils.makeAccessible(field); Object value = ReflectionUtils.getField(field, target); - return (T)value; + return (T) value; } @SpringBootConfiguration @EnableAutoConfiguration - @EnableFeignClients(clients = {ApacheHttpClientConfigurationTestApp.FooClient.class}) + @EnableFeignClients(clients = { + ApacheHttpClientConfigurationTestApp.FooClient.class }) static class ApacheHttpClientConfigurationTestApp { - static class MyApacheHttpClientConnectionManagerFactory extends DefaultApacheHttpClientConnectionManagerFactory { + @FeignClient(name = "foo", serviceId = "foo") + interface FooClient { + + } + + static class MyApacheHttpClientConnectionManagerFactory + extends DefaultApacheHttpClientConnectionManagerFactory { + @Override - public HttpClientConnectionManager newConnectionManager(boolean disableSslValidation, int maxTotalConnections, int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit, RegistryBuilder registry) { + public HttpClientConnectionManager newConnectionManager( + boolean disableSslValidation, int maxTotalConnections, + int maxConnectionsPerRoute, long timeToLive, TimeUnit timeUnit, + RegistryBuilder registry) { return mock(PoolingHttpClientConnectionManager.class); } + } static class MyApacheHttpClientFactory extends DefaultApacheHttpClientFactory { - public MyApacheHttpClientFactory(HttpClientBuilder builder) { + + MyApacheHttpClientFactory(HttpClientBuilder builder) { super(builder); } @Override public HttpClientBuilder createBuilder() { - CloseableHttpClient client = mock(CloseableHttpClient.class); + CloseableHttpClient client = mock(CloseableHttpClient.class); CloseableHttpResponse response = mock(CloseableHttpResponse.class); StatusLine statusLine = mock(StatusLine.class); doReturn(200).when(statusLine).getStatusCode(); @@ -134,21 +147,25 @@ public class ApacheHttpClientConfigurationTests { Header[] headers = new BasicHeader[0]; doReturn(headers).when(response).getAllHeaders(); try { - Mockito.doReturn(response).when(client).execute(any(HttpUriRequest.class)); - } catch (IOException e) { + Mockito.doReturn(response).when(client) + .execute(any(HttpUriRequest.class)); + } + catch (IOException e) { e.printStackTrace(); } HttpClientBuilder builder = mock(HttpClientBuilder.class); Mockito.doReturn(client).when(builder).build(); return builder; } + } @Configuration static class MyConfig { @Bean - public ApacheHttpClientFactory apacheHttpClientFactory(HttpClientBuilder builder) { + public ApacheHttpClientFactory apacheHttpClientFactory( + HttpClientBuilder builder) { return new MyApacheHttpClientFactory(builder); } @@ -159,9 +176,6 @@ public class ApacheHttpClientConfigurationTests { } - @FeignClient(name="foo", serviceId = "foo") - interface FooClient {} } } - diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/NoSecurityConfiguration.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/NoSecurityConfiguration.java index 8769d85b..af6a0831 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/NoSecurityConfiguration.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/NoSecurityConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.test; @@ -23,11 +22,10 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur @Configuration public class NoSecurityConfiguration extends WebSecurityConfigurerAdapter { + @Override protected void configure(HttpSecurity http) throws Exception { - http.authorizeRequests() - .anyRequest().permitAll() - .and() - .csrf().disable(); + http.authorizeRequests().anyRequest().permitAll().and().csrf().disable(); } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/OkHttpClientConfigurationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/OkHttpClientConfigurationTests.java index e1071858..1f470128 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/OkHttpClientConfigurationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/OkHttpClientConfigurationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,7 +12,6 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.test; @@ -20,9 +19,13 @@ package org.springframework.cloud.openfeign.test; import java.lang.reflect.Field; import java.util.concurrent.TimeUnit; +import feign.Client; +import okhttp3.ConnectionPool; +import okhttp3.OkHttpClient; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.MockingDetails; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.SpringBootConfiguration; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; @@ -39,21 +42,18 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.util.ReflectionUtils; import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.mockingDetails; -import feign.Client; -import okhttp3.ConnectionPool; -import okhttp3.OkHttpClient; - /** * @author Ryan Baxter */ @RunWith(SpringRunner.class) -@SpringBootTest(properties = {"feign.okhttp.enabled: true", - "spring.cloud.httpclientfactories.ok.enabled: true", "ribbon.eureka.enabled = false", "ribbon.okhttp.enabled: true", - "feign.okhttp.enabled: true", "ribbon.httpclient.enabled: false", "feign.httpclient.enabled: false"}) +@SpringBootTest(properties = { "feign.okhttp.enabled: true", + "spring.cloud.httpclientfactories.ok.enabled: true", + "ribbon.eureka.enabled = false", "ribbon.okhttp.enabled: true", + "feign.okhttp.enabled: true", "ribbon.httpclient.enabled: false", + "feign.httpclient.enabled: false" }) @DirtiesContext public class OkHttpClientConfigurationTests { @@ -68,46 +68,41 @@ public class OkHttpClientConfigurationTests { @Test public void testFactories() { - assertThat(connectionPoolFactory).isInstanceOf(OkHttpClientConnectionPoolFactory.class); - assertThat(connectionPoolFactory).isInstanceOf(TestConfig.MyOkHttpClientConnectionPoolFactory.class); - assertThat(okHttpClientFactory).isInstanceOf(OkHttpClientFactory.class); - assertThat(okHttpClientFactory).isInstanceOf(TestConfig.MyOkHttpClientFactory.class); + assertThat(this.connectionPoolFactory) + .isInstanceOf(OkHttpClientConnectionPoolFactory.class); + assertThat(this.connectionPoolFactory) + .isInstanceOf(TestConfig.MyOkHttpClientConnectionPoolFactory.class); + assertThat(this.okHttpClientFactory).isInstanceOf(OkHttpClientFactory.class); + assertThat(this.okHttpClientFactory) + .isInstanceOf(TestConfig.MyOkHttpClientFactory.class); } @Test public void testHttpClientWithFeign() { - Client delegate = feignClient.getDelegate(); - assertTrue(feign.okhttp.OkHttpClient.class.isInstance(delegate)); - feign.okhttp.OkHttpClient okHttpClient = (feign.okhttp.OkHttpClient)delegate; + Client delegate = this.feignClient.getDelegate(); + assertThat(feign.okhttp.OkHttpClient.class.isInstance(delegate)).isTrue(); + feign.okhttp.OkHttpClient okHttpClient = (feign.okhttp.OkHttpClient) delegate; OkHttpClient httpClient = getField(okHttpClient, "delegate"); MockingDetails httpClientDetails = mockingDetails(httpClient); - assertTrue(httpClientDetails.isMock()); + assertThat(httpClientDetails.isMock()).isTrue(); } protected T getField(Object target, String name) { Field field = ReflectionUtils.findField(target.getClass(), name); ReflectionUtils.makeAccessible(field); Object value = ReflectionUtils.getField(field, target); - return (T)value; + return (T) value; + } + + @FeignClient(name = "foo", serviceId = "foo") + interface FooClient { + } @SpringBootConfiguration @EnableAutoConfiguration static class TestConfig { - static class MyOkHttpClientConnectionPoolFactory extends DefaultOkHttpClientConnectionPoolFactory { - @Override - public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, TimeUnit timeUnit) { - return new ConnectionPool(); - } - } - - static class MyOkHttpClientFactory extends DefaultOkHttpClientFactory { - public MyOkHttpClientFactory(OkHttpClient.Builder builder) { - super(builder); - } - } - @Bean public OkHttpClientConnectionPoolFactory connectionPoolFactory() { return new MyOkHttpClientConnectionPoolFactory(); @@ -120,10 +115,28 @@ public class OkHttpClientConfigurationTests { @Bean public OkHttpClient client() { - return mock(OkHttpClient.class); - } + return mock(OkHttpClient.class); + } + + static class MyOkHttpClientConnectionPoolFactory + extends DefaultOkHttpClientConnectionPoolFactory { + + @Override + public ConnectionPool create(int maxIdleConnections, long keepAliveDuration, + TimeUnit timeUnit) { + return new ConnectionPool(); + } + + } + + static class MyOkHttpClientFactory extends DefaultOkHttpClientFactory { + + MyOkHttpClientFactory(OkHttpClient.Builder builder) { + super(builder); + } + + } + } - @FeignClient(name="foo", serviceId = "foo") - interface FooClient {} } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/TestAutoConfiguration.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/TestAutoConfiguration.java index 7410ca48..e552c29a 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/TestAutoConfiguration.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/test/TestAutoConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,17 +34,18 @@ import org.springframework.security.provisioning.InMemoryUserDetailsManager; * @author Spencer Gibb */ @Configuration -@Import({NoopDiscoveryClientAutoConfiguration.class}) +@Import({ NoopDiscoveryClientAutoConfiguration.class }) @AutoConfigureBefore(SecurityAutoConfiguration.class) public class TestAutoConfiguration { public static final String USER = "user"; + public static final String PASSWORD = "{noop}password"; @Configuration @Order(Ordered.HIGHEST_PRECEDENCE) - protected static class TestSecurityConfiguration extends WebSecurityConfigurerAdapter { - + protected static class TestSecurityConfiguration + extends WebSecurityConfigurerAdapter { TestSecurityConfiguration() { super(true); @@ -53,18 +54,18 @@ public class TestAutoConfiguration { @Bean public UserDetailsService userDetailsService() { InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager(); - manager.createUser(User.withUsername(USER).password(PASSWORD).roles("USER").build()); + manager.createUser( + User.withUsername(USER).password(PASSWORD).roles("USER").build()); return manager; } @Override protected void configure(HttpSecurity http) throws Exception { // super.configure(http); - http.antMatcher("/proxy-username") - .httpBasic() - .and() - .authorizeRequests().antMatchers("/**").permitAll(); + http.antMatcher("/proxy-username").httpBasic().and().authorizeRequests() + .antMatchers("/**").permitAll(); } } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/testclients/TestClient.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/testclients/TestClient.java index 77cbf14c..ac6ca990 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/testclients/TestClient.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/testclients/TestClient.java @@ -1,20 +1,19 @@ /* + * Copyright 2013-2019 the original author or authors. * - * * Copyright 2013-2016 the original author or authors. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.springframework.cloud.openfeign.testclients; import org.springframework.cloud.openfeign.FeignClient; @@ -26,7 +25,8 @@ import org.springframework.web.bind.annotation.RequestMethod; */ @FeignClient(name = "localapp") public interface TestClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") String getHello(); -} +} diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientNotPrimaryTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientNotPrimaryTests.java index 76784fa8..eba0338c 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientNotPrimaryTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientNotPrimaryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,24 +12,26 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.valid; import java.util.List; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import feign.Logger; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.cloud.openfeign.EnableFeignClients; -import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.StaticServerList; +import org.springframework.cloud.openfeign.EnableFeignClients; +import org.springframework.cloud.openfeign.FeignClient; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Primary; @@ -39,20 +41,15 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - -import feign.Logger; - import static org.assertj.core.api.Assertions.assertThat; -import static org.junit.Assert.assertNull; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * @author Spencer Gibb * @author Jakub Narloch */ @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = FeignClientNotPrimaryTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { +@SpringBootTest(classes = FeignClientNotPrimaryTests.Application.class, webEnvironment = RANDOM_PORT, value = { "spring.application.name=feignclientnotprimarytest", "logging.level.org.springframework.cloud.openfeign.valid=DEBUG", "feign.httpclient.enabled=false", "feign.okhttp.enabled=false" }) @@ -60,8 +57,11 @@ import static org.junit.Assert.assertNull; public class FeignClientNotPrimaryTests { public static final String HELLO_WORLD_1 = "hello world 1"; + public static final String OI_TERRA_2 = "oi terra 2"; + public static final String MYHEADER1 = "myheader1"; + public static final String MYHEADER2 = "myheader2"; @Value("${local.server.port}") @@ -73,8 +73,26 @@ public class FeignClientNotPrimaryTests { @Autowired private List testClients; + @Test + public void testClientType() { + assertThat(this.testClient).as("testClient was of wrong type") + .isInstanceOf(PrimaryTestClient.class); + } + + @Test + public void testClientCount() { + assertThat(this.testClients).as("testClients was wrong").hasSize(2); + } + + @Test + public void testSimpleType() { + Hello hello = this.testClient.getHello(); + assertThat(hello).as("hello was null").isNull(); + } + @FeignClient(name = "localapp", primary = false) protected interface TestClient { + @RequestMapping(method = RequestMethod.GET, path = "/hello") Hello getHello(); @@ -83,8 +101,8 @@ public class FeignClientNotPrimaryTests { @Configuration @EnableAutoConfiguration @RestController - @EnableFeignClients(clients = { TestClient.class} , - defaultConfiguration = TestDefaultFeignConfig.class) + @EnableFeignClients(clients = { + TestClient.class }, defaultConfiguration = TestDefaultFeignConfig.class) @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class) protected static class Application { @@ -101,53 +119,44 @@ public class FeignClientNotPrimaryTests { } - @Test - public void testClientType() { - assertThat(this.testClient).as("testClient was of wrong type").isInstanceOf(PrimaryTestClient.class); - } - - @Test - public void testClientCount() { - assertThat(this.testClients).as("testClients was wrong").hasSize(2); - } - - @Test - public void testSimpleType() { - Hello hello = this.testClient.getHello(); - assertNull("hello was null", hello); - } - protected static class PrimaryTestClient implements TestClient { + @Override public Hello getHello() { return null; } + } public static class Hello { + private String message; - public Hello() {} + public Hello() { + } public Hello(String message) { this.message = message; } public String getMessage() { - return message; + return this.message; } public void setMessage(String message) { this.message = message; } + } @Configuration public static class TestDefaultFeignConfig { + @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } + } // Load balancer with fixed server list for "local" pointing to localhost @@ -163,4 +172,5 @@ public class FeignClientNotPrimaryTests { } } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientTests.java index d526c32a..f870871b 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -31,8 +31,25 @@ import java.util.Optional; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import com.netflix.hystrix.HystrixCommand; +import com.netflix.hystrix.HystrixCommandGroupKey; +import com.netflix.hystrix.HystrixCommandKey; +import com.netflix.hystrix.exception.HystrixRuntimeException; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import feign.Client; +import feign.Feign; +import feign.Logger; +import feign.RequestInterceptor; +import feign.RequestTemplate; +import feign.Target; +import feign.hystrix.FallbackFactory; +import feign.hystrix.SetterFactory; import org.junit.Test; import org.junit.runner.RunWith; +import rx.Observable; +import rx.Single; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.beans.factory.annotation.Value; @@ -40,14 +57,14 @@ import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.cloud.netflix.ribbon.RibbonClients; +import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.FeignFormatterRegistrar; import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; import org.springframework.cloud.openfeign.support.FallbackCommand; -import org.springframework.cloud.netflix.ribbon.RibbonClient; -import org.springframework.cloud.netflix.ribbon.RibbonClients; -import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -67,32 +84,7 @@ import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; -import com.netflix.hystrix.HystrixCommand; -import com.netflix.hystrix.HystrixCommandGroupKey; -import com.netflix.hystrix.HystrixCommandKey; -import com.netflix.hystrix.exception.HystrixRuntimeException; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - import static org.assertj.core.api.Assertions.assertThat; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertThat; -import static org.junit.Assert.assertTrue; - -import feign.Client; -import feign.Feign; -import feign.Logger; -import feign.RequestInterceptor; -import feign.RequestTemplate; -import feign.Target; -import feign.hystrix.FallbackFactory; -import feign.hystrix.SetterFactory; -import rx.Observable; -import rx.Single; /** * @author Spencer Gibb @@ -105,15 +97,28 @@ import rx.Single; "spring.application.name=feignclienttest", "logging.level.org.springframework.cloud.openfeign.valid=DEBUG", "feign.httpclient.enabled=false", "feign.okhttp.enabled=false", - "feign.hystrix.enabled=true"}) + "feign.hystrix.enabled=true" }) @DirtiesContext public class FeignClientTests { public static final String HELLO_WORLD_1 = "hello world 1"; + public static final String OI_TERRA_2 = "oi terra 2"; + public static final String MYHEADER1 = "myheader1"; + public static final String MYHEADER2 = "myheader2"; + @Autowired + HystrixClient hystrixClient; + + @Autowired + @Qualifier("localapp3FeignClient") + HystrixClient namedHystrixClient; + + @Autowired + HystrixSetterFactoryClient hystrixSetterFactoryClient; + @Value("${local.server.port}") private int port = 0; @@ -129,9 +134,6 @@ public class FeignClientTests { @Autowired private Client feignClient; - @Autowired - HystrixClient hystrixClient; - @Autowired private HystrixClientWithFallBackFactory hystrixClientWithFallBackFactory; @@ -141,37 +143,298 @@ public class FeignClientTests { @Autowired private NullHystrixClientWithFallBackFactory nullHystrixClientWithFallBackFactory; - @Autowired - @Qualifier("localapp3FeignClient") - HystrixClient namedHystrixClient; + private static ArrayList getHelloList() { + ArrayList hellos = new ArrayList<>(); + hellos.add(new Hello(HELLO_WORLD_1)); + hellos.add(new Hello(OI_TERRA_2)); + return hellos; + } - @Autowired - HystrixSetterFactoryClient hystrixSetterFactoryClient; + @Test + public void testClient() { + assertThat(this.testClient).as("testClient was null").isNotNull(); + assertThat(Proxy.isProxyClass(this.testClient.getClass())) + .as("testClient is not a java Proxy").isTrue(); + InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient); + assertThat(invocationHandler).as("invocationHandler was null").isNotNull(); + } + + @Test + public void testRequestMappingClassLevelPropertyReplacement() { + Hello hello = this.testClient.getHelloUsingPropertyPlaceHolder(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match").isEqualTo(new Hello(OI_TERRA_2)); + } + + @Test + public void testSimpleType() { + Hello hello = this.testClient.getHello(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello(HELLO_WORLD_1)); + } + + @Test + public void testOptional() { + Optional hello = this.testClient.getOptionalHello(); + assertThat(hello).isNotNull().isPresent().contains(new Hello(HELLO_WORLD_1)); + } + + @Test + public void testGenericType() { + List hellos = this.testClient.getHellos(); + assertThat(hellos).as("hellos was null").isNotNull(); + assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos); + } + + @Test + public void testRequestInterceptors() { + List headers = this.testClient.getHelloHeaders(); + assertThat(headers).as("headers was null").isNotNull(); + assertThat(headers.contains("myheader1value")) + .as("headers didn't contain myheader1value").isTrue(); + assertThat(headers.contains("myheader2value")) + .as("headers didn't contain myheader2value").isTrue(); + } + + @Test + public void testHeaderPlaceholders() { + String header = this.testClient.getHelloHeadersPlaceholders(); + assertThat(header).as("header was null").isNotNull(); + assertThat(header).as("header was wrong").isEqualTo("myPlaceholderHeaderValue"); + } + + @Test + public void testFeignClientType() throws IllegalAccessException { + assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class); + LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient; + Client delegate = client.getDelegate(); + assertThat(delegate).isInstanceOf(Client.Default.class); + } + + @Test + public void testServiceId() { + assertThat(this.testClientServiceId).as("testClientServiceId was null") + .isNotNull(); + final Hello hello = this.testClientServiceId.getHello(); + assertThat(hello).as("The hello response was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello(HELLO_WORLD_1)); + } + + @Test + public void testParams() { + List list = Arrays.asList("a", "1", "test"); + List params = this.testClient.getParams(list); + assertThat(params).as("params was null").isNotNull(); + assertThat(params.size()).as("params size was wrong").isEqualTo(list.size()); + } + + @Test + public void testFormattedParams() { + List list = Arrays.asList(LocalDate.of(2001, 1, 1), + LocalDate.of(2018, 6, 10)); + List params = this.testClient.getFormattedParams(list); + assertThat(params).as("params was null").isNotNull(); + assertThat(params).as("params not converted correctly").isEqualTo(list); + } + + @Test + public void testHystrixCommand() throws NoSuchMethodException { + HystrixCommand> command = this.testClient.getHellosHystrix(); + assertThat(command).as("command was null").isNotNull(); + assertThat(command.getCommandGroup().name()).as( + "Hystrix command group name should match the name of the feign client") + .isEqualTo("localapp"); + String configKey = Feign.configKey(TestClient.class, + TestClient.class.getMethod("getHellosHystrix", (Class[]) null)); + assertThat(command.getCommandKey().name()) + .as("Hystrix command key name should match the feign config key") + .isEqualTo(configKey); + List hellos = command.execute(); + assertThat(hellos).as("hellos was null").isNotNull(); + assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos); + } + + @Test + public void testSingle() { + Single single = this.testClient.getHelloSingle(); + assertThat(single).as("single was null").isNotNull(); + Hello hello = single.toBlocking().value(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello(HELLO_WORLD_1)); + } + + @Test + public void testNoContentResponse() { + ResponseEntity response = this.testClient.noContent(); + assertThat(response).as("response was null").isNotNull(); + assertThat(response.getStatusCode()).as("status code was wrong") + .isEqualTo(HttpStatus.NO_CONTENT); + } + + @Test + public void testHeadResponse() { + ResponseEntity response = this.testClient.head(); + assertThat(response).as("response was null").isNotNull(); + assertThat(response.getStatusCode()).as("status code was wrong") + .isEqualTo(HttpStatus.OK); + } + + @Test + public void testHttpEntity() { + HttpEntity entity = this.testClient.getHelloEntity(); + assertThat(entity).as("entity was null").isNotNull(); + Hello hello = entity.getBody(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello(HELLO_WORLD_1)); + } + + @Test + public void testMoreComplexHeader() { + String response = this.testClient.moreComplexContentType("{\"value\":\"OK\"}"); + assertThat(response).as("response was null").isNotNull(); + assertThat(response).as("didn't respond with {\"value\":\"OK\"}") + .isEqualTo("{\"value\":\"OK\"}"); + } + + @Test + public void testDecodeNotFound() { + ResponseEntity response = this.decodingTestClient.notFound(); + assertThat(response).as("response was null").isNotNull(); + assertThat(response.getStatusCode()).as("status code was wrong") + .isEqualTo(HttpStatus.NOT_FOUND); + assertThat(response.getBody()).as("response body was not null").isNull(); + } + + @Test + public void testOptionalNotFound() { + Optional s = this.decodingTestClient.optional(); + assertThat(s).isNotPresent(); + } + + @Test + public void testConvertingExpander() { + assertThat(this.testClient.getToString(Arg.A)).isEqualTo(Arg.A.toString()); + assertThat(this.testClient.getToString(Arg.B)).isEqualTo(Arg.B.toString()); + + assertThat(this.testClient.getToString(new OtherArg("foo"))).isEqualTo("bar"); + List args = new ArrayList<>(); + args.add(new OtherArg("foo")); + args.add(new OtherArg("goo")); + List expectedResult = new ArrayList<>(); + expectedResult.add("bar"); + expectedResult.add("goo"); + assertThat(this.testClient.getToString(args)).isEqualTo(expectedResult); + } + + @Test + public void testHystrixFallbackWorks() { + Hello hello = this.hystrixClient.fail(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello.getMessage()).as("message was wrong").isEqualTo("fallback"); + } + + @Test + public void testHystrixFallbackSingle() { + Single single = this.hystrixClient.failSingle(); + assertThat(single).as("single was null").isNotNull(); + Hello hello = single.toBlocking().value(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello.getMessage()).as("message was wrong") + .isEqualTo("fallbacksingle"); + } + + @Test + public void testHystrixFallbackCommand() { + HystrixCommand command = this.hystrixClient.failCommand(); + assertThat(command).as("command was null").isNotNull(); + Hello hello = command.execute(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello.getMessage()).as("message was wrong") + .isEqualTo("fallbackcommand"); + } + + @Test + public void testHystrixFallbackObservable() { + Observable observable = this.hystrixClient.failObservable(); + assertThat(observable).as("observable was null").isNotNull(); + Hello hello = observable.toBlocking().first(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello.getMessage()).as("message was wrong") + .isEqualTo("fallbackobservable"); + } + + @Test + public void testHystrixFallbackFuture() throws Exception { + Future future = this.hystrixClient.failFuture(); + assertThat(future).as("future was null").isNotNull(); + Hello hello = future.get(1, TimeUnit.SECONDS); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello.getMessage()).as("message was wrong") + .isEqualTo("fallbackfuture"); + } + + @Test + public void testHystrixClientWithFallBackFactory() throws Exception { + Hello hello = this.hystrixClientWithFallBackFactory.fail(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello.getMessage()).as("hello#message was null").isNotNull(); + assertThat(hello.getMessage().contains("500")).as( + "hello#message did not contain the cause (status code) of the fallback invocation") + .isTrue(); + } + + @Test(expected = HystrixRuntimeException.class) + public void testInvalidTypeHystrixFallbackFactory() throws Exception { + this.invalidTypeHystrixClientWithFallBackFactory.fail(); + } + + @Test(expected = HystrixRuntimeException.class) + public void testNullHystrixFallbackFactory() throws Exception { + this.nullHystrixClientWithFallBackFactory.fail(); + } + + @Test + public void namedFeignClientWorks() { + assertThat(this.namedHystrixClient).as("namedHystrixClient was null").isNotNull(); + } + + @Test + public void testHystrixSetterFactory() { + HystrixCommand> command = this.hystrixSetterFactoryClient + .getHellosHystrix(); + assertThat(command).as("command was null").isNotNull(); + String setterPrefix = TestHystrixSetterFactoryClientConfig.SETTER_PREFIX; + assertThat(command.getCommandGroup().name()).as( + "Hystrix command group name should match the name of the feign client with a prefix of " + + setterPrefix) + .isEqualTo(setterPrefix + "localapp5"); + assertThat(command.getCommandKey().name()).as( + "Hystrix command key name should match the request method (space) request path with a prefix of " + + setterPrefix) + .isEqualTo(setterPrefix + "GET /hellos"); + List hellos = command.execute(); + assertThat(hellos).as("hellos was null").isNotNull(); + assertThat(getHelloList()).as("hellos didn't match").isEqualTo(hellos); + } protected enum Arg { + A, B; @Override public String toString() { return name().toLowerCase(Locale.ENGLISH); } - } - protected static class OtherArg { - public final String value; - - public OtherArg(String value) { - this.value = value; - } - - @Override - public String toString() { - return value; - } } @FeignClient(name = "localapp", configuration = TestClientConfig.class) protected interface TestClient { + @RequestMapping(method = RequestMethod.GET, path = "/hello") Hello getHello(); @@ -201,8 +464,7 @@ public class FeignClientTests { @RequestMapping(method = RequestMethod.GET, path = "/formattedparams") List getFormattedParams( - @RequestParam("params") - @DateTimeFormat(pattern = "dd-MM-yyyy") List params); + @RequestParam("params") @DateTimeFormat(pattern = "dd-MM-yyyy") List params); @RequestMapping(method = RequestMethod.GET, path = "/hellos") HystrixCommand> getHellosHystrix(); @@ -227,6 +489,93 @@ public class FeignClientTests { @RequestMapping(method = RequestMethod.GET, path = "/tostringcollection") Collection getToString(@RequestParam("arg") Collection args); + + } + + @FeignClient(name = "localapp1") + protected interface TestClientServiceId { + + @RequestMapping(method = RequestMethod.GET, path = "/hello") + Hello getHello(); + + } + + @FeignClient(name = "localapp2", decode404 = true) + protected interface DecodingTestClient { + + @RequestMapping(method = RequestMethod.GET, path = "/notFound") + ResponseEntity notFound(); + + @RequestMapping(method = RequestMethod.GET, path = "/notFound") + Optional optional(); + + } + + @FeignClient(name = "localapp3", fallback = HystrixClientFallback.class) + protected interface HystrixClient { + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + Single failSingle(); + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + Hello fail(); + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + HystrixCommand failCommand(); + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + Observable failObservable(); + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + Future failFuture(); + + } + + @FeignClient(name = "localapp4", fallbackFactory = HystrixClientFallbackFactory.class) + protected interface HystrixClientWithFallBackFactory { + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + Hello fail(); + + } + + @FeignClient(name = "localapp6", fallbackFactory = InvalidTypeHystrixClientFallbackFactory.class) + protected interface InvalidTypeHystrixClientWithFallBackFactory { + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + Hello fail(); + + } + + @FeignClient(name = "localapp7", fallbackFactory = NullHystrixClientFallbackFactory.class) + protected interface NullHystrixClientWithFallBackFactory { + + @RequestMapping(method = RequestMethod.GET, path = "/fail") + Hello fail(); + + } + + @FeignClient(name = "localapp5", configuration = TestHystrixSetterFactoryClientConfig.class) + protected interface HystrixSetterFactoryClient { + + @RequestMapping(method = RequestMethod.GET, path = "/hellos") + HystrixCommand> getHellosHystrix(); + + } + + protected static class OtherArg { + + public final String value; + + public OtherArg(String value) { + this.value = value; + } + + @Override + public String toString() { + return this.value; + } + } public static class TestClientConfig { @@ -250,82 +599,34 @@ public class FeignClientTests { } }; } + } - @FeignClient(name = "localapp1") - protected interface TestClientServiceId { - @RequestMapping(method = RequestMethod.GET, path = "/hello") - Hello getHello(); - } - - @FeignClient(name = "localapp2", decode404 = true) - protected interface DecodingTestClient { - @RequestMapping(method = RequestMethod.GET, path = "/notFound") - ResponseEntity notFound(); - - @RequestMapping(method = RequestMethod.GET, path = "/notFound") - Optional optional(); - } - - @FeignClient(name = "localapp3", fallback = HystrixClientFallback.class) - protected interface HystrixClient { - @RequestMapping(method = RequestMethod.GET, path = "/fail") - Single failSingle(); - - @RequestMapping(method = RequestMethod.GET, path = "/fail") - Hello fail(); - - @RequestMapping(method = RequestMethod.GET, path = "/fail") - HystrixCommand failCommand(); - - @RequestMapping(method = RequestMethod.GET, path = "/fail") - Observable failObservable(); - - @RequestMapping(method = RequestMethod.GET, path = "/fail") - Future failFuture(); - } - - @FeignClient(name = "localapp4", fallbackFactory = HystrixClientFallbackFactory.class) - protected interface HystrixClientWithFallBackFactory { - - @RequestMapping(method = RequestMethod.GET, path = "/fail") - Hello fail(); - } - - @FeignClient(name = "localapp6", fallbackFactory = InvalidTypeHystrixClientFallbackFactory.class) - protected interface InvalidTypeHystrixClientWithFallBackFactory { - - @RequestMapping(method = RequestMethod.GET, path = "/fail") - Hello fail(); - } - - @FeignClient(name = "localapp7", fallbackFactory = NullHystrixClientFallbackFactory.class) - protected interface NullHystrixClientWithFallBackFactory { - - @RequestMapping(method = RequestMethod.GET, path = "/fail") - Hello fail(); - } - - static class HystrixClientFallbackFactory implements FallbackFactory { + static class HystrixClientFallbackFactory + implements FallbackFactory { @Override public HystrixClientWithFallBackFactory create(final Throwable cause) { return new HystrixClientWithFallBackFactory() { @Override public Hello fail() { - assertNotNull("Cause was null", cause); - return new Hello("Hello from the fallback side: " + cause.getMessage()); + assertThat(cause).isNotNull().as("Cause was null"); + return new Hello( + "Hello from the fallback side: " + cause.getMessage()); } }; } + } - static class InvalidTypeHystrixClientFallbackFactory implements FallbackFactory { + static class InvalidTypeHystrixClientFallbackFactory + implements FallbackFactory { @Override public String create(final Throwable cause) { return "hello"; } + } static class NullHystrixClientFallbackFactory implements FallbackFactory { @@ -334,9 +635,11 @@ public class FeignClientTests { public String create(final Throwable cause) { return null; } + } static class HystrixClientFallback implements HystrixClient { + @Override public Hello fail() { return new Hello("fallback"); @@ -361,56 +664,59 @@ public class FeignClientTests { public Future failFuture() { return new FallbackCommand<>(new Hello("fallbackfuture")).queue(); } - } - @FeignClient(name = "localapp5", configuration = TestHystrixSetterFactoryClientConfig.class) - protected interface HystrixSetterFactoryClient { - @RequestMapping(method = RequestMethod.GET, path = "/hellos") - HystrixCommand> getHellosHystrix(); } public static class TestHystrixSetterFactoryClientConfig { + public static final String SETTER_PREFIX = "SETTER-"; + @Bean public SetterFactory commandKeyIsRequestLineSetterFactory() { return new SetterFactory() { - @Override public HystrixCommand.Setter create(Target target, - Method method) { + @Override + public HystrixCommand.Setter create(Target target, Method method) { String groupKey = SETTER_PREFIX + target.name(); RequestMapping requestMapping = method - .getAnnotation(RequestMapping.class); - String commandKey = - SETTER_PREFIX + requestMapping.method()[0] + " " + requestMapping - .path()[0]; + .getAnnotation(RequestMapping.class); + String commandKey = SETTER_PREFIX + requestMapping.method()[0] + " " + + requestMapping.path()[0]; return HystrixCommand.Setter - .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) - .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)); + .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey)) + .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey)); } }; } + } @Configuration @EnableAutoConfiguration @RestController @EnableFeignClients(clients = { TestClientServiceId.class, TestClient.class, - DecodingTestClient.class, HystrixClient.class, HystrixClientWithFallBackFactory.class, - HystrixSetterFactoryClient.class, InvalidTypeHystrixClientWithFallBackFactory.class, - NullHystrixClientWithFallBackFactory.class}, - defaultConfiguration = TestDefaultFeignConfig.class) + DecodingTestClient.class, HystrixClient.class, + HystrixClientWithFallBackFactory.class, HystrixSetterFactoryClient.class, + InvalidTypeHystrixClientWithFallBackFactory.class, + NullHystrixClientWithFallBackFactory.class }, defaultConfiguration = TestDefaultFeignConfig.class) @RibbonClients({ - @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp2", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp4", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp5", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp2", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp3", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp4", configuration = LocalRibbonClientConfiguration.class), + @RibbonClient(name = "localapp5", configuration = LocalRibbonClientConfiguration.class), @RibbonClient(name = "localapp6", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp7", configuration = LocalRibbonClientConfiguration.class) - }) + @RibbonClient(name = "localapp7", configuration = LocalRibbonClientConfiguration.class) }) @Import(NoSecurityConfiguration.class) protected static class Application { + public static void main(String[] args) { + new SpringApplicationBuilder(Application.class) + .properties("spring.application.name=feignclienttest", + "management.contextPath=/admin") + .run(args); + } + // needs to be in parent context to test multiple HystrixClient beans @Bean public HystrixClientFallback hystrixClientFallback() { @@ -442,7 +748,7 @@ public class FeignClientTests { @Override public String print(OtherArg object, Locale locale) { - if("foo".equals(object.value)) { + if ("foo".equals(object.value)) { return "bar"; } return object.value; @@ -504,8 +810,7 @@ public class FeignClientTests { @RequestMapping(method = RequestMethod.GET, path = "/formattedparams") public List getFormattedParams( - @RequestParam("params") - @DateTimeFormat(pattern = "dd-MM-yyyy") List params) { + @RequestParam("params") @DateTimeFormat(pattern = "dd-MM-yyyy") List params) { return params; } @@ -530,9 +835,11 @@ public class FeignClientTests { } @RequestMapping(method = RequestMethod.POST, consumes = "application/vnd.io.spring.cloud.test.v1+json", produces = "application/vnd.io.spring.cloud.test.v1+json", path = "/complex") - String complex(@RequestBody String body, @RequestHeader("Content-Length") int contentLength) { + String complex(@RequestBody String body, + @RequestHeader("Content-Length") int contentLength) { if (contentLength <= 0) { - throw new IllegalArgumentException("Invalid Content-Length "+ contentLength); + throw new IllegalArgumentException( + "Invalid Content-Length " + contentLength); } return body; } @@ -550,290 +857,16 @@ public class FeignClientTests { @RequestMapping(method = RequestMethod.GET, path = "/tostringcollection") Collection getToString(@RequestParam("arg") Collection args) { List result = new ArrayList<>(); - for(OtherArg arg : args) { + for (OtherArg arg : args) { result.add(arg.value); } return result; } - public static void main(String[] args) { - new SpringApplicationBuilder(Application.class) - .properties("spring.application.name=feignclienttest", - "management.contextPath=/admin") - .run(args); - } - } - - private static ArrayList getHelloList() { - ArrayList hellos = new ArrayList<>(); - hellos.add(new Hello(HELLO_WORLD_1)); - hellos.add(new Hello(OI_TERRA_2)); - return hellos; - } - - @Test - public void testClient() { - assertNotNull("testClient was null", this.testClient); - assertTrue("testClient is not a java Proxy", - Proxy.isProxyClass(this.testClient.getClass())); - InvocationHandler invocationHandler = Proxy.getInvocationHandler(this.testClient); - assertNotNull("invocationHandler was null", invocationHandler); - } - - @Test - public void testRequestMappingClassLevelPropertyReplacement() { - Hello hello = this.testClient.getHelloUsingPropertyPlaceHolder(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello(OI_TERRA_2), hello); - } - - @Test - public void testSimpleType() { - Hello hello = this.testClient.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello(HELLO_WORLD_1), hello); - } - - @Test - public void testOptional() { - Optional hello = this.testClient.getOptionalHello(); - assertThat(hello) - .isNotNull() - .isPresent() - .contains(new Hello(HELLO_WORLD_1)); - } - - @Test - public void testGenericType() { - List hellos = this.testClient.getHellos(); - assertNotNull("hellos was null", hellos); - assertEquals("hellos didn't match", hellos, getHelloList()); - } - - @Test - public void testRequestInterceptors() { - List headers = this.testClient.getHelloHeaders(); - assertNotNull("headers was null", headers); - assertTrue("headers didn't contain myheader1value", - headers.contains("myheader1value")); - assertTrue("headers didn't contain myheader2value", - headers.contains("myheader2value")); - } - - @Test - public void testHeaderPlaceholders() { - String header = this.testClient.getHelloHeadersPlaceholders(); - assertNotNull("header was null", header); - assertEquals("header was wrong", "myPlaceholderHeaderValue", header); - } - - @Test - public void testFeignClientType() throws IllegalAccessException { - assertThat(this.feignClient, is(instanceOf(LoadBalancerFeignClient.class))); - LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient; - Client delegate = client.getDelegate(); - assertThat(delegate, is(instanceOf(feign.Client.Default.class))); - } - - @Test - public void testServiceId() { - assertNotNull("testClientServiceId was null", this.testClientServiceId); - final Hello hello = this.testClientServiceId.getHello(); - assertNotNull("The hello response was null", hello); - assertEquals("first hello didn't match", new Hello(HELLO_WORLD_1), hello); - } - - @Test - public void testParams() { - List list = Arrays.asList("a", "1", "test"); - List params = this.testClient.getParams(list); - assertNotNull("params was null", params); - assertEquals("params size was wrong", list.size(), params.size()); - } - - @Test - public void testFormattedParams() { - List list = Arrays.asList( - LocalDate.of(2001, 1, 1), LocalDate.of(2018, 6, 10)); - List params = this.testClient.getFormattedParams(list); - assertNotNull("params was null", params); - assertEquals("params not converted correctly", list, params); - } - - @Test - public void testHystrixCommand() throws NoSuchMethodException { - HystrixCommand> command = this.testClient.getHellosHystrix(); - assertNotNull("command was null", command); - assertEquals( - "Hystrix command group name should match the name of the feign client", - "localapp", command.getCommandGroup().name()); - String configKey = Feign.configKey(TestClient.class, - TestClient.class.getMethod("getHellosHystrix", (Class[]) null)); - assertEquals("Hystrix command key name should match the feign config key", - configKey, command.getCommandKey().name()); - List hellos = command.execute(); - assertNotNull("hellos was null", hellos); - assertEquals("hellos didn't match", hellos, getHelloList()); - } - - @Test - public void testSingle() { - Single single = this.testClient.getHelloSingle(); - assertNotNull("single was null", single); - Hello hello = single.toBlocking().value(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello(HELLO_WORLD_1), hello); - } - - @Test - public void testNoContentResponse() { - ResponseEntity response = testClient.noContent(); - assertNotNull("response was null", response); - assertEquals("status code was wrong", HttpStatus.NO_CONTENT, - response.getStatusCode()); - } - - @Test - public void testHeadResponse() { - ResponseEntity response = testClient.head(); - assertNotNull("response was null", response); - assertEquals("status code was wrong", HttpStatus.OK, response.getStatusCode()); - } - - @Test - public void testHttpEntity() { - HttpEntity entity = testClient.getHelloEntity(); - assertNotNull("entity was null", entity); - Hello hello = entity.getBody(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello(HELLO_WORLD_1), hello); - } - - @Test - public void testMoreComplexHeader() { - String response = testClient.moreComplexContentType("{\"value\":\"OK\"}"); - assertNotNull("response was null", response); - assertEquals("didn't respond with {\"value\":\"OK\"}", "{\"value\":\"OK\"}", - response); - } - - @Test - public void testDecodeNotFound() { - ResponseEntity response = decodingTestClient.notFound(); - assertNotNull("response was null", response); - assertEquals("status code was wrong", HttpStatus.NOT_FOUND, - response.getStatusCode()); - assertNull("response body was not null", response.getBody()); - } - - @Test - public void testOptionalNotFound() { - Optional s = decodingTestClient.optional(); - assertThat(s).isNotPresent(); - } - - @Test - public void testConvertingExpander() { - assertEquals(Arg.A.toString(), testClient.getToString(Arg.A)); - assertEquals(Arg.B.toString(), testClient.getToString(Arg.B)); - - assertEquals("bar", testClient.getToString(new OtherArg("foo"))); - List args = new ArrayList<>(); - args.add(new OtherArg("foo")); - args.add(new OtherArg("goo")); - List expectedResult = new ArrayList<>(); - expectedResult.add("bar"); - expectedResult.add("goo"); - assertEquals(expectedResult, testClient.getToString(args)); - } - - @Test - public void testHystrixFallbackWorks() { - Hello hello = hystrixClient.fail(); - assertNotNull("hello was null", hello); - assertEquals("message was wrong", "fallback", hello.getMessage()); - } - - @Test - public void testHystrixFallbackSingle() { - Single single = hystrixClient.failSingle(); - assertNotNull("single was null", single); - Hello hello = single.toBlocking().value(); - assertNotNull("hello was null", hello); - assertEquals("message was wrong", "fallbacksingle", hello.getMessage()); - } - - @Test - public void testHystrixFallbackCommand() { - HystrixCommand command = hystrixClient.failCommand(); - assertNotNull("command was null", command); - Hello hello = command.execute(); - assertNotNull("hello was null", hello); - assertEquals("message was wrong", "fallbackcommand", hello.getMessage()); - } - - @Test - public void testHystrixFallbackObservable() { - Observable observable = hystrixClient.failObservable(); - assertNotNull("observable was null", observable); - Hello hello = observable.toBlocking().first(); - assertNotNull("hello was null", hello); - assertEquals("message was wrong", "fallbackobservable", hello.getMessage()); - } - - @Test - public void testHystrixFallbackFuture() throws Exception { - Future future = hystrixClient.failFuture(); - assertNotNull("future was null", future); - Hello hello = future.get(1, TimeUnit.SECONDS); - assertNotNull("hello was null", hello); - assertEquals("message was wrong", "fallbackfuture", hello.getMessage()); - } - - @Test - public void testHystrixClientWithFallBackFactory() throws Exception { - Hello hello = hystrixClientWithFallBackFactory.fail(); - assertNotNull("hello was null", hello); - assertNotNull("hello#message was null", hello.getMessage()); - assertTrue("hello#message did not contain the cause (status code) of the fallback invocation", - hello.getMessage().contains("500")); - } - - @Test(expected = HystrixRuntimeException.class) - public void testInvalidTypeHystrixFallbackFactory() throws Exception { - invalidTypeHystrixClientWithFallBackFactory.fail(); - } - - @Test(expected = HystrixRuntimeException.class) - public void testNullHystrixFallbackFactory() throws Exception { - nullHystrixClientWithFallBackFactory.fail(); - } - - @Test - public void namedFeignClientWorks() { - assertNotNull("namedHystrixClient was null", this.namedHystrixClient); - } - - @Test - public void testHystrixSetterFactory() { - HystrixCommand> command = this.hystrixSetterFactoryClient - .getHellosHystrix(); - assertNotNull("command was null", command); - String setterPrefix = TestHystrixSetterFactoryClientConfig.SETTER_PREFIX; - assertEquals( - "Hystrix command group name should match the name of the feign client with a prefix of " - + setterPrefix, setterPrefix + "localapp5", - command.getCommandGroup().name()); - assertEquals( - "Hystrix command key name should match the request method (space) request path with a prefix of " - + setterPrefix, setterPrefix + "GET /hellos", - command.getCommandKey().name()); - List hellos = command.execute(); - assertNotNull("hellos was null", hellos); - assertEquals("hellos didn't match", hellos, getHelloList()); } public static class Hello { + private String message; public Hello() { @@ -844,7 +877,7 @@ public class FeignClientTests { } public String getMessage() { - return message; + return this.message; } public void setMessage(String message) { @@ -853,24 +886,31 @@ public class FeignClientTests { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Hello that = (Hello) o; - return Objects.equals(message, that.message); + return Objects.equals(this.message, that.message); } @Override public int hashCode() { - return Objects.hash(message); + return Objects.hash(this.message); } + } @Configuration public static class TestDefaultFeignConfig { + @Bean Logger.Level feignLoggerLevel() { return Logger.Level.FULL; } + } // Load balancer with fixed server list for "local" pointing to localhost @@ -886,4 +926,5 @@ public class FeignClientTests { } } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientValidationTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientValidationTests.java index fe91ffbe..1824d3d5 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientValidationTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignClientValidationTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,20 +17,21 @@ package org.springframework.cloud.openfeign.valid; import org.junit.Test; + import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration; import org.springframework.cloud.commons.httpclient.HttpClientConfiguration; +import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignAutoConfiguration; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.ribbon.FeignRibbonClientAutoConfiguration; -import org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Dave Syer @@ -41,67 +42,72 @@ public class FeignClientValidationTests { public void validNotLoadBalanced() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( GoodUrlConfiguration.class); - assertNotNull(context.getBean(GoodUrlConfiguration.Client.class)); + assertThat(context.getBean(GoodUrlConfiguration.Client.class)).isNotNull(); context.close(); } - @Configuration - @Import({FeignAutoConfiguration.class, HttpClientConfiguration.class}) - @EnableFeignClients(clients = GoodUrlConfiguration.Client.class) - protected static class GoodUrlConfiguration { - - @FeignClient(name="example", url="http://example.com") - interface Client { - @RequestMapping(method = RequestMethod.GET, value = "/") - @Deprecated - String get(); - } - - } - @Test public void validPlaceholder() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( PlaceholderUrlConfiguration.class); - assertNotNull(context.getBean(PlaceholderUrlConfiguration.Client.class)); + assertThat(context.getBean(PlaceholderUrlConfiguration.Client.class)).isNotNull(); context.close(); } - @Configuration - @Import({FeignAutoConfiguration.class, HttpClientConfiguration.class}) - @EnableFeignClients(clients = PlaceholderUrlConfiguration.Client.class) - protected static class PlaceholderUrlConfiguration { - - @FeignClient(name="example", url="${feignClient.url:http://example.com}") - interface Client { - @RequestMapping(method = RequestMethod.GET, value = "/") - @Deprecated - String get(); - } - - } - @Test public void validLoadBalanced() { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext( - LoadBalancerAutoConfiguration.class, - RibbonAutoConfiguration.class, + LoadBalancerAutoConfiguration.class, RibbonAutoConfiguration.class, FeignRibbonClientAutoConfiguration.class, GoodServiceIdConfiguration.class); - assertNotNull(context.getBean(GoodServiceIdConfiguration.Client.class)); + assertThat(context.getBean(GoodServiceIdConfiguration.Client.class)).isNotNull(); context.close(); } @Configuration - @Import({FeignAutoConfiguration.class, HttpClientConfiguration.class}) + @Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class }) + @EnableFeignClients(clients = GoodUrlConfiguration.Client.class) + protected static class GoodUrlConfiguration { + + @FeignClient(name = "example", url = "http://example.com") + interface Client { + + @RequestMapping(method = RequestMethod.GET, value = "/") + @Deprecated + String get(); + + } + + } + + @Configuration + @Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class }) + @EnableFeignClients(clients = PlaceholderUrlConfiguration.Client.class) + protected static class PlaceholderUrlConfiguration { + + @FeignClient(name = "example", url = "${feignClient.url:http://example.com}") + interface Client { + + @RequestMapping(method = RequestMethod.GET, value = "/") + @Deprecated + String get(); + + } + + } + + @Configuration + @Import({ FeignAutoConfiguration.class, HttpClientConfiguration.class }) @EnableFeignClients(clients = GoodServiceIdConfiguration.Client.class) protected static class GoodServiceIdConfiguration { @FeignClient("foo") interface Client { + @RequestMapping(method = RequestMethod.GET, value = "/") @Deprecated String get(); + } } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignHttpClientTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignHttpClientTests.java index d2139536..4d11f7bf 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignHttpClientTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignHttpClientTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2015 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,19 +18,23 @@ package org.springframework.cloud.openfeign.valid; import java.util.Objects; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import feign.Client; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.boot.web.server.LocalServerPort; +import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.RibbonClients; +import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; -import org.springframework.cloud.netflix.ribbon.RibbonClient; -import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -46,18 +50,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; - -import feign.Client; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb @@ -81,25 +74,63 @@ public class FeignHttpClientTests { @Autowired private UserClient userClient; + @Test + public void testSimpleType() { + Hello hello = this.testClient.getHello(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello("hello world 1")); + } + + @Test + public void testPatch() { + ResponseEntity response = this.testClient.patchHello(new Hello("foo")); + assertThat(response).isNotNull(); + String header = response.getHeaders().getFirst("X-Hello"); + assertThat(header).isEqualTo("hello world patch"); + } + + @Test + public void testFeignClientType() throws IllegalAccessException { + assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class); + LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient; + Client delegate = client.getDelegate(); + assertThat(delegate).isInstanceOf(feign.httpclient.ApacheHttpClient.class); + } + + @Test + public void testFeignInheritanceSupport() { + assertThat(this.userClient).as("UserClient was null").isNotNull(); + final User user = this.userClient.getUser(1); + assertThat(user).as("Returned user was null").isNotNull(); + assertThat(new User("John Smith")).as("Users were different").isEqualTo(user); + } + @FeignClient("localapp") protected interface TestClient extends BaseTestClient { + } protected interface BaseTestClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello", produces = MediaType.APPLICATION_JSON_VALUE) Hello getHello(); @RequestMapping(method = RequestMethod.PATCH, value = "/hellop", consumes = "application/json") ResponseEntity patchHello(Hello hello); + } protected interface UserService { + @RequestMapping(method = RequestMethod.GET, value = "/users/{id}", produces = MediaType.APPLICATION_JSON_VALUE) User getUser(@PathVariable("id") long id); + } @FeignClient("localapp1") protected interface UserClient extends UserService { + } @Configuration @@ -108,8 +139,7 @@ public class FeignHttpClientTests { @EnableFeignClients(clients = { TestClient.class, UserClient.class }) @RibbonClients({ @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class) - }) + @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class) }) @Import(NoSecurityConfiguration.class) protected static class Application implements UserService { @@ -120,12 +150,14 @@ public class FeignHttpClientTests { @RequestMapping(method = RequestMethod.PATCH, value = "/hellop") public ResponseEntity patchHello(@RequestBody Hello hello, - @RequestHeader("Content-Length") int contentLength) { + @RequestHeader("Content-Length") int contentLength) { if (contentLength <= 0) { - throw new IllegalArgumentException("Invalid Content-Length "+ contentLength); + throw new IllegalArgumentException( + "Invalid Content-Length " + contentLength); } if (!hello.getMessage().equals("foo")) { - throw new IllegalArgumentException("Invalid Hello: " + hello.getMessage()); + throw new IllegalArgumentException( + "Invalid Hello: " + hello.getMessage()); } return ResponseEntity.ok().header("X-Hello", "hello world patch").build(); } @@ -137,48 +169,19 @@ public class FeignHttpClientTests { } - @Test - public void testSimpleType() { - Hello hello = this.testClient.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello("hello world 1"), hello); - } - - @Test - public void testPatch() { - ResponseEntity response = this.testClient.patchHello(new Hello("foo")); - assertThat(response, is(notNullValue())); - String header = response.getHeaders().getFirst("X-Hello"); - assertThat(header, equalTo("hello world patch")); - } - - @Test - public void testFeignClientType() throws IllegalAccessException { - assertThat(this.feignClient, is(instanceOf(LoadBalancerFeignClient.class))); - LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient; - Client delegate = client.getDelegate(); - assertThat(delegate, is(instanceOf(feign.httpclient.ApacheHttpClient.class))); - } - - @Test - public void testFeignInheritanceSupport() { - assertNotNull("UserClient was null", this.userClient); - final User user = this.userClient.getUser(1); - assertNotNull("Returned user was null", user); - assertEquals("Users were different", user, new User("John Smith")); - } - public static class Hello { + private String message; - public Hello() {} + public Hello() { + } public Hello(String message) { this.message = message; } public String getMessage() { - return message; + return this.message; } public void setMessage(String message) { @@ -187,29 +190,36 @@ public class FeignHttpClientTests { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Hello that = (Hello) o; - return Objects.equals(message, that.message); + return Objects.equals(this.message, that.message); } @Override public int hashCode() { - return Objects.hash(message); + return Objects.hash(this.message); } + } public static class User { + private String name; - public User() {} + public User() { + } public User(String name) { this.name = name; } public String getName() { - return name; + return this.name; } public void setName(String name) { @@ -218,16 +228,21 @@ public class FeignHttpClientTests { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } User that = (User) o; - return Objects.equals(name, that.name); + return Objects.equals(this.name, that.name); } @Override public int hashCode() { - return Objects.hash(name); + return Objects.hash(this.name); } + } // Load balancer with fixed server list for "local" pointing to localhost @@ -243,4 +258,5 @@ public class FeignHttpClientTests { } } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignOkHttpTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignOkHttpTests.java index 2da09b25..d6a5483b 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignOkHttpTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/FeignOkHttpTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2017 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -12,24 +12,29 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * */ package org.springframework.cloud.openfeign.valid; +import java.util.Objects; + +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; +import feign.Client; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.RibbonClients; +import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient; -import org.springframework.cloud.netflix.ribbon.RibbonClient; -import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -44,20 +49,7 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.instanceOf; -import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.notNullValue; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertThat; - -import feign.Client; - -import java.util.Objects; +import static org.assertj.core.api.Assertions.assertThat; /** * @author Spencer Gibb @@ -82,25 +74,63 @@ public class FeignOkHttpTests { @Autowired private UserClient userClient; + @Test + public void testSimpleType() { + Hello hello = this.testClient.getHello(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match") + .isEqualTo(new Hello("hello world 1")); + } + + @Test + public void testPatch() { + ResponseEntity response = this.testClient.patchHello(new Hello("foo")); + assertThat(response).isNotNull(); + String header = response.getHeaders().getFirst("X-Hello"); + assertThat(header).isEqualTo("hello world patch"); + } + + @Test + public void testFeignClientType() throws IllegalAccessException { + assertThat(this.feignClient).isInstanceOf(LoadBalancerFeignClient.class); + LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient; + Client delegate = client.getDelegate(); + assertThat(delegate).isInstanceOf(feign.okhttp.OkHttpClient.class); + } + + @Test + public void testFeignInheritanceSupport() { + assertThat(this.userClient).as("UserClient was null").isNotNull(); + final User user = this.userClient.getUser(1); + assertThat(user).as("Returned user was null").isNotNull(); + assertThat(new User("John Smith")).as("Users were different").isEqualTo(user); + } + @FeignClient("localapp") protected interface TestClient extends BaseTestClient { + } protected interface BaseTestClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") Hello getHello(); @RequestMapping(method = RequestMethod.PATCH, value = "/hellop", consumes = "application/json") ResponseEntity patchHello(Hello hello); + } protected interface UserService { + @RequestMapping(method = RequestMethod.GET, value = "/users/{id}") User getUser(@PathVariable("id") long id); + } @FeignClient("localapp1") protected interface UserClient extends UserService { + } @Configuration @@ -109,8 +139,7 @@ public class FeignOkHttpTests { @EnableFeignClients(clients = { TestClient.class, UserClient.class }) @RibbonClients({ @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class), - @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class) - }) + @RibbonClient(name = "localapp1", configuration = LocalRibbonClientConfiguration.class) }) @Import(NoSecurityConfiguration.class) protected static class Application implements UserService { @@ -123,10 +152,12 @@ public class FeignOkHttpTests { public ResponseEntity patchHello(@RequestBody Hello hello, @RequestHeader("Content-Length") int contentLength) { if (contentLength <= 0) { - throw new IllegalArgumentException("Invalid Content-Length "+ contentLength); + throw new IllegalArgumentException( + "Invalid Content-Length " + contentLength); } if (!hello.getMessage().equals("foo")) { - throw new IllegalArgumentException("Invalid Hello: " + hello.getMessage()); + throw new IllegalArgumentException( + "Invalid Hello: " + hello.getMessage()); } return ResponseEntity.ok().header("X-Hello", "hello world patch").build(); } @@ -138,38 +169,8 @@ public class FeignOkHttpTests { } - @Test - public void testSimpleType() { - Hello hello = this.testClient.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", new Hello("hello world 1"), hello); - } - - @Test - public void testPatch() { - ResponseEntity response = this.testClient.patchHello(new Hello("foo")); - assertThat(response, is(notNullValue())); - String header = response.getHeaders().getFirst("X-Hello"); - assertThat(header, equalTo("hello world patch")); - } - - @Test - public void testFeignClientType() throws IllegalAccessException { - assertThat(this.feignClient, is(instanceOf(LoadBalancerFeignClient.class))); - LoadBalancerFeignClient client = (LoadBalancerFeignClient) this.feignClient; - Client delegate = client.getDelegate(); - assertThat(delegate, is(instanceOf(feign.okhttp.OkHttpClient.class))); - } - - @Test - public void testFeignInheritanceSupport() { - assertNotNull("UserClient was null", this.userClient); - final User user = this.userClient.getUser(1); - assertNotNull("Returned user was null", user); - assertEquals("Users were different", user, new User("John Smith")); - } - public static class Hello { + private String message; public Hello() { @@ -180,7 +181,7 @@ public class FeignOkHttpTests { } public String getMessage() { - return message; + return this.message; } public void setMessage(String message) { @@ -189,19 +190,25 @@ public class FeignOkHttpTests { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } Hello that = (Hello) o; - return Objects.equals(message, that.message); + return Objects.equals(this.message, that.message); } @Override public int hashCode() { - return Objects.hash(message); + return Objects.hash(this.message); } + } public static class User { + private String name; public User() { @@ -212,7 +219,7 @@ public class FeignOkHttpTests { } public String getName() { - return name; + return this.name; } public void setName(String name) { @@ -221,16 +228,21 @@ public class FeignOkHttpTests { @Override public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } User that = (User) o; - return Objects.equals(name, that.name); + return Objects.equals(this.name, that.name); } @Override public int hashCode() { - return Objects.hash(name); + return Objects.hash(this.name); } + } // Load balancer with fixed server list for "local" pointing to localhost @@ -246,4 +258,5 @@ public class FeignOkHttpTests { } } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/scanning/FeignClientEnvVarTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/scanning/FeignClientEnvVarTests.java index 9423b7d4..01c22415 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/scanning/FeignClientEnvVarTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/scanning/FeignClientEnvVarTests.java @@ -1,33 +1,35 @@ /* + * Copyright 2013-2019 the original author or authors. * - * * Copyright 2013-2016 the original author or authors. - * * - * * Licensed under the Apache License, Version 2.0 (the "License"); - * * you may not use this file except in compliance with the License. - * * You may obtain a copy of the License at - * * - * * http://www.apache.org/licenses/LICENSE-2.0 - * * - * * Unless required by applicable law or agreed to in writing, software - * * distributed under the License is distributed on an "AS IS" BASIS, - * * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * * See the License for the specific language governing permissions and - * * limitations under the License. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ + package org.springframework.cloud.openfeign.valid.scanning; +import com.netflix.loadbalancer.Server; +import com.netflix.loadbalancer.ServerList; import org.junit.Test; import org.junit.runner.RunWith; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.cloud.netflix.ribbon.RibbonClient; +import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.cloud.openfeign.test.NoSecurityConfiguration; import org.springframework.cloud.openfeign.testclients.TestClient; -import org.springframework.cloud.netflix.ribbon.RibbonClient; -import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; @@ -37,17 +39,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import com.netflix.loadbalancer.Server; -import com.netflix.loadbalancer.ServerList; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * @author Ryan Baxter */ @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = FeignClientEnvVarTests.Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, value = { +@SpringBootTest(classes = FeignClientEnvVarTests.Application.class, webEnvironment = RANDOM_PORT, value = { "spring.application.name=feignclienttest", "feign.httpclient.enabled=false", "basepackage=org.springframework.cloud.openfeign.testclients" }) @DirtiesContext @@ -59,22 +58,23 @@ public class FeignClientEnvVarTests { @Test public void testSimpleType() { String hello = this.testClient.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", "hello world 1", hello); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match").isEqualTo("hello world 1"); } @Configuration @EnableAutoConfiguration @RestController - @EnableFeignClients(basePackages = {"${basepackage}"}) + @EnableFeignClients(basePackages = { "${basepackage}" }) @RibbonClient(name = "localapp", configuration = LocalRibbonClientConfiguration.class) @Import(NoSecurityConfiguration.class) protected static class Application { + @RequestMapping(method = RequestMethod.GET, value = "/hello") public String getHello() { return "hello world 1"; } - + } // Load balancer with fixed server list for "local" pointing to localhost @@ -90,4 +90,5 @@ public class FeignClientEnvVarTests { } } + } diff --git a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/scanning/FeignClientScanningTests.java b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/scanning/FeignClientScanningTests.java index 80e6aab0..598272d2 100644 --- a/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/scanning/FeignClientScanningTests.java +++ b/spring-cloud-openfeign-core/src/test/java/org/springframework/cloud/openfeign/valid/scanning/FeignClientScanningTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2018 the original author or authors. + * Copyright 2013-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,7 +26,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.EnableAutoConfiguration; import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.cloud.netflix.ribbon.StaticServerList; import org.springframework.cloud.openfeign.EnableFeignClients; @@ -41,14 +40,14 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RestController; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.assertj.core.api.Assertions.assertThat; +import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT; /** * @author Spencer Gibb */ @RunWith(SpringJUnit4ClassRunner.class) -@SpringBootTest(classes = FeignClientScanningTests.Application.class, webEnvironment = WebEnvironment.RANDOM_PORT, value = { +@SpringBootTest(classes = FeignClientScanningTests.Application.class, webEnvironment = RANDOM_PORT, value = { "spring.application.name=feignclienttest", "feign.httpclient.enabled=false" }) @DirtiesContext public class FeignClientScanningTests { @@ -66,16 +65,34 @@ public class FeignClientScanningTests { @SuppressWarnings("unused") private Client feignClient; + @Test + public void testSimpleType() { + String hello = this.testClient.getHello(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match").isEqualTo("hello world 1"); + } + + @Test + public void testSimpleTypeByKey() { + String hello = this.testClientByKey.getHello(); + assertThat(hello).as("hello was null").isNotNull(); + assertThat(hello).as("first hello didn't match").isEqualTo("hello world 1"); + } + @FeignClient("localapp123") protected interface TestClient { + @RequestMapping(method = RequestMethod.GET, value = "/hello") String getHello(); + } @FeignClient("${feignClient.localappName}") protected interface TestClientByKey { + @RequestMapping(method = RequestMethod.GET, value = "/hello") String getHello(); + } @Configuration @@ -85,6 +102,7 @@ public class FeignClientScanningTests { @RibbonClients(defaultConfiguration = LocalRibbonClientConfiguration.class) @Import(NoSecurityConfiguration.class) protected static class Application { + @RequestMapping(method = RequestMethod.GET, value = "/hello") public String getHello() { return "hello world 1"; @@ -92,20 +110,6 @@ public class FeignClientScanningTests { } - @Test - public void testSimpleType() { - String hello = this.testClient.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", "hello world 1", hello); - } - - @Test - public void testSimpleTypeByKey() { - String hello = this.testClientByKey.getHello(); - assertNotNull("hello was null", hello); - assertEquals("first hello didn't match", "hello world 1", hello); - } - // Load balancer with fixed server list for "local" pointing to localhost @Configuration public static class LocalRibbonClientConfiguration { @@ -119,4 +123,5 @@ public class FeignClientScanningTests { } } + } diff --git a/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties b/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties index 0fe10a1e..b5b13c8f 100644 --- a/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties +++ b/spring-cloud-openfeign-core/src/test/resources/feign-properties.properties @@ -1,21 +1,15 @@ # This configuration used by test class FeignClientUsingPropertiesTests - logging.level.org.springframework.cloud.openfeign=debug - feign.client.default-to-properties=true feign.client.default-config=default - feign.client.config.default.connectTimeout=5000 feign.client.config.default.readTimeout=5000 feign.client.config.default.loggerLevel=full feign.client.config.default.errorDecoder=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.DefaultErrorDecoder feign.client.config.default.retryer=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.NoRetryer feign.client.config.default.decode404=true - feign.client.config.foo.requestInterceptors[0]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FooRequestInterceptor feign.client.config.foo.requestInterceptors[1]=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.BarRequestInterceptor - feign.client.config.bar.connectTimeout=1000 feign.client.config.bar.readTimeout=1000 - -feign.client.config.form.encoder=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FormEncoder \ No newline at end of file +feign.client.config.form.encoder=org.springframework.cloud.openfeign.FeignClientUsingPropertiesTests.FormEncoder diff --git a/spring-cloud-openfeign-dependencies/pom.xml b/spring-cloud-openfeign-dependencies/pom.xml index b306e509..1c8357b4 100644 --- a/spring-cloud-openfeign-dependencies/pom.xml +++ b/spring-cloud-openfeign-dependencies/pom.xml @@ -1,5 +1,6 @@ - 4.0.0 diff --git a/spring-cloud-starter-openfeign/pom.xml b/spring-cloud-starter-openfeign/pom.xml index a4b81a1e..bc44a4e7 100644 --- a/spring-cloud-starter-openfeign/pom.xml +++ b/spring-cloud-starter-openfeign/pom.xml @@ -1,5 +1,6 @@ - + 4.0.0 org.springframework.cloud @@ -62,4 +63,4 @@ true - \ No newline at end of file + diff --git a/spring-cloud-starter-openfeign/src/main/resources/META-INF/spring.provides b/spring-cloud-starter-openfeign/src/main/resources/META-INF/spring.provides index ee5b1c80..e9617ce8 100644 --- a/spring-cloud-starter-openfeign/src/main/resources/META-INF/spring.provides +++ b/spring-cloud-starter-openfeign/src/main/resources/META-INF/spring.provides @@ -1 +1 @@ -provides: spring-cloud-openfeign-core \ No newline at end of file +provides: spring-cloud-openfeign-core diff --git a/src/checkstyle/checkstyle-suppressions.xml b/src/checkstyle/checkstyle-suppressions.xml new file mode 100644 index 00000000..d55c3a1d --- /dev/null +++ b/src/checkstyle/checkstyle-suppressions.xml @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + +