[
  {
    "path": ".gitignore",
    "content": "target/\n!.mvn/wrapper/maven-wrapper.jar\n\n### STS ###\n.apt_generated\n.classpath\n.factorypath\n.project\n.settings\n.springBeans\n\n### IntelliJ IDEA ###\n.idea\n*.iws\n*.iml\n*.ipr\n\n### NetBeans ###\nnbproject/private/\nbuild/\nnbbuild/\ndist/\nnbdist/\n.nb-gradle/"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "README.md",
    "content": "Happy to announce that from now Java Reactive Feign client is officially backed by Playtika. All development will be conducted in Playtika fork https://github.com/Playtika/feign-reactive\n\n[Subscribe to stay up to date 🙂](https://www.youtube.com/channel/UCAIRpdkVAj1RT6butHUV9yg)\n\n# feign-reactive\n\n[ ![Download](https://api.bintray.com/packages/kptfh/feign-reactive/client/images/download.svg) ](https://bintray.com/kptfh/feign-reactive/client/_latestVersion)\n\nUse Feign with Spring WebFlux\n\n## Overview\n\nImplementation of Feign on Spring WebClient. Brings you the best of two worlds together : \nconcise syntax of Feign to write client side API on fast, asynchronous and\nnon-blocking HTTP client of Spring WebClient.\n\n## Modules\n  \n  **_feign-reactor-core_** : base classes and interfaces that should allow to implement alternative reactor Feign\n  \n  **_feign-reactor-webclient_** : Spring WebClient based implementation of reactor Feign \n  \n  **_feign-reactor-cloud_** : Spring Cloud implementation of reactor Feign (Ribbon/Hystrix)\n  \n  **_feign-reactor-rx2_** : Rx2 compatible implementation of reactor Feign (depends on feign-reactor-webclient)\n  \n  **_feign-reactor-jetty_** : experimental Reactive Jetty client based implementation of reactor Feign (doesn't depend on feign-reactor-webclient). In future will allow to write pure Rx2 version.\n  - have greater reactivity level then Spring WebClient. By default don't collect body to list instead starts sending request body as stream. \n  - starts receiving reactive response before all reactive request body has been sent\n  - process Flux<`String`> correctly in request and response body  \n\n## Usage\n\nWrite Feign API as usual, but every method of interface\n - may accept `org.reactivestreams.Publisher` as body\n - must return `reactor.core.publisher.Mono` or `reactor.core.publisher.Flux`.\n\n```java\n@Headers({ \"Accept: application/json\" })\npublic interface IcecreamServiceApi {\n\n  @RequestLine(\"GET /icecream/flavors\")\n  Flux<Flavor> getAvailableFlavors();\n\n  @RequestLine(\"GET /icecream/mixins\")\n  Flux<Mixin> getAvailableMixins();\n\n  @RequestLine(\"POST /icecream/orders\")\n  @Headers(\"Content-Type: application/json\")\n  Mono<Bill> makeOrder(IceCreamOrder order);\n\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  Mono<IceCreamOrder> findOrder(@Param(\"orderId\") int orderId);\n\n  @RequestLine(\"POST /icecream/bills/pay\")\n  @Headers(\"Content-Type: application/json\")\n  Mono<Void> payBill(Publisher<Bill> bill);\n}\n```\nBuild the client :\n\n```java\n\n/* Create instance of your API */\nIcecreamServiceApi client = ReactiveFeign\n    .builder()\n    .target(IcecreamServiceApi.class, \"http://www.icecreame.com\")\n\n/* Execute nonblocking requests */\nFlux<Flavor> flavors = icecreamApi.getAvailableFlavors();\nFlux<Mixin> mixins = icecreamApi.getAvailableMixins();\n```\n\nor cloud aware client :\n\n```java\n IcecreamServiceApi client = CloudReactiveFeign.<IcecreamServiceApi>builder()\n    .setFallback(new TestInterface() {\n        @Override\n        public Mono<String> get() {\n            return Mono.just(\"fallback\");\n        }\n    })\n    .setLoadBalancerCommand(\n         LoadBalancerCommand.builder()\n                 .withLoadBalancer(AbstractLoadBalancer.class.cast(getNamedLoadBalancer(serviceName)))\n                 .withRetryHandler(new DefaultLoadBalancerRetryHandler(1, 1, true))\n                 .build()\n    )\n    .target(IcecreamServiceApi.class, \"http://\" + serviceName);\n\n/* Execute nonblocking requests */\nFlux<Flavor> flavors = icecreamApi.getAvailableFlavors();\nFlux<Mixin> mixins = icecreamApi.getAvailableMixins();\n```\n\n## Rx2 Usage \n\nWrite Feign API as usual, but every method of interface\n - may accept `Flowable`, `Observable`, `Single` or `Maybe` as body\n - must return `Flowable`, `Observable`, `Single` or `Maybe`.\n\n```java\n@Headers({\"Accept: application/json\"})\npublic interface IcecreamServiceApi {\n\n  @RequestLine(\"GET /icecream/flavors\")\n  Flowable<Flavor> getAvailableFlavors();\n\n  @RequestLine(\"GET /icecream/mixins\")\n  Observable<Mixin> getAvailableMixins();\n\n  @RequestLine(\"POST /icecream/orders\")\n  @Headers(\"Content-Type: application/json\")\n  Single<Bill> makeOrder(IceCreamOrder order);\n\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  Maybe<IceCreamOrder> findOrder(@Param(\"orderId\") int orderId);\n\n  @RequestLine(\"POST /icecream/bills/pay\")\n  @Headers(\"Content-Type: application/json\")\n  Single<Long> payBill(Bill bill);\n```\nBuild the client :\n\n```java\n\n/* Create instance of your API */\nIcecreamServiceApi client = Rx2ReactiveFeign\n    .builder()\n    .target(IcecreamServiceApi.class, \"http://www.icecreame.com\")\n\n/* Execute nonblocking requests */\nFlowable<Flavor> flavors = icecreamApi.getAvailableFlavors();\nObservable<Mixin> mixins = icecreamApi.getAvailableMixins();\n```\n\n## Maven\n\n```xml\n<repositories>\n    <repository>\n        <id>bintray-kptfh-feign-reactive</id>\n        <name>bintray</name>\n        <url>https://dl.bintray.com/kptfh/feign-reactive</url>\n    </repository>\n</repositories>\n...\n<dependencies>\n    ...\n    \n    <dependency>\n        <groupId>io.github.reactivefeign</groupId>\n        <artifactId>feign-reactor-cloud</artifactId>\n        <version>1.0.0</version>\n    </dependency>\n    \n    or if you don't need cloud specific functionality\n    \n    <dependency>\n        <groupId>io.github.reactivefeign</groupId>\n        <artifactId>feign-reactor-webclient</artifactId>\n        <version>1.0.0</version>\n    </dependency>\n    \n    or if you tend to use Rx2 interfaces\n    \n    <dependency>\n            <groupId>io.github.reactivefeign</groupId>\n            <artifactId>feign-reactor-rx2</artifactId>\n            <version>1.0.0</version>\n        </dependency>\n    ...\n</dependencies>\n```\n\n## License\n\nLibrary distributed under Apache License Version 2.0.\n"
  },
  {
    "path": "feign-reactor-cloud/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.github.reactivefeign</groupId>\n        <artifactId>feign-reactor</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>feign-reactor-cloud</artifactId>\n\n    <properties>\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>io.github.reactivefeign</groupId>\n            <artifactId>feign-reactor-webclient</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.netflix.hystrix</groupId>\n            <artifactId>hystrix-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.netflix.archaius</groupId>\n            <artifactId>archaius-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.netflix.ribbon</groupId>\n            <artifactId>ribbon-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.netflix.ribbon</groupId>\n            <artifactId>ribbon-loadbalancer</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.reactivex</groupId>\n            <artifactId>rxjava</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.reactivex</groupId>\n            <artifactId>rxjava-reactive-streams</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n        </dependency>\n\n        <!-- Tests -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.projectreactor</groupId>\n            <artifactId>reactor-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.tomakehurst</groupId>\n            <artifactId>wiremock</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "feign-reactor-cloud/src/main/java/reactivefeign/cloud/CloudReactiveFeign.java",
    "content": "package reactivefeign.cloud;\n\nimport com.netflix.client.ClientFactory;\nimport com.netflix.client.RetryHandler;\nimport com.netflix.hystrix.HystrixCommandGroupKey;\nimport com.netflix.hystrix.HystrixCommandKey;\nimport com.netflix.hystrix.HystrixObservableCommand;\nimport com.netflix.loadbalancer.reactive.LoadBalancerCommand;\nimport feign.Contract;\nimport feign.InvocationHandlerFactory;\nimport feign.MethodMetadata;\nimport feign.Target;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.ReactiveRetryPolicy;\nimport reactivefeign.client.ReactiveHttpRequestInterceptor;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactivefeign.client.statushandler.ReactiveStatusHandler;\nimport reactivefeign.cloud.methodhandler.HystrixMethodHandlerFactory;\nimport reactivefeign.cloud.publisher.RibbonPublisherClient;\nimport reactivefeign.methodhandler.MethodHandlerFactory;\nimport reactivefeign.publisher.PublisherClientFactory;\nimport reactivefeign.publisher.PublisherHttpClient;\nimport reactivefeign.webclient.WebReactiveFeign;\n\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\n\nimport static reactivefeign.utils.FeignUtils.returnPublisherType;\n\n/**\n * Allows to specify ribbon {@link LoadBalancerCommand}\n * and HystrixObservableCommand.Setter with fallback factory.\n *\n * @author Sergii Karpenko\n */\npublic class CloudReactiveFeign extends ReactiveFeign {\n\n    private static final Logger logger = LoggerFactory.getLogger(CloudReactiveFeign.class);\n\n    private CloudReactiveFeign(ReactiveFeign.ParseHandlersByName targetToHandlersByName, InvocationHandlerFactory factory) {\n        super(targetToHandlersByName, factory);\n    }\n\n    public static <T> Builder<T> builder() {\n        return new Builder<>();\n    }\n\n    public static <T> Builder<T> builder(WebClient webClient) {\n        return new Builder<>(webClient);\n    }\n\n    public static class Builder<T> extends WebReactiveFeign.Builder<T> {\n\n        private boolean hystrixEnabled = true;\n        private SetterFactory commandSetterFactory = new DefaultSetterFactory();\n        private Function<Throwable, ? extends T> fallbackFactory;\n        private Function<String, LoadBalancerCommand<Object>> loadBalancerCommandFactory = s -> null;\n\n        protected Builder() {\n            super();\n        }\n\n        protected Builder(WebClient webClient) {\n            super(webClient);\n        }\n\n        public void disableHystrix() {\n            this.hystrixEnabled = false;\n        }\n\n        public Builder<T> setHystrixCommandSetterFactory(SetterFactory commandSetterFactory) {\n            this.commandSetterFactory = commandSetterFactory;\n            return this;\n        }\n\n        public Builder<T> setFallback(T fallback) {\n            return setFallbackFactory(throwable -> fallback);\n        }\n\n        public Builder<T> setFallbackFactory(Function<Throwable, ? extends T> fallbackFactory) {\n            this.fallbackFactory = fallbackFactory;\n            return this;\n        }\n\n        public Builder<T> enableLoadBalancer(){\n            return setLoadBalancerCommandFactory(serviceName ->\n                    LoadBalancerCommand.builder()\n                            .withLoadBalancer(ClientFactory.getNamedLoadBalancer(serviceName))\n                            .build());\n        }\n\n        public Builder<T> enableLoadBalancer(RetryHandler retryHandler){\n            if(retryHandler.getMaxRetriesOnSameServer() > 0){\n                logger.warn(\"Use retryWhen(ReactiveRetryPolicy retryPolicy) \" +\n                        \"as it allow to configure retry delays (backoff)\");\n            }\n            return setLoadBalancerCommandFactory(serviceName ->\n                    LoadBalancerCommand.builder()\n                    .withLoadBalancer(ClientFactory.getNamedLoadBalancer(serviceName))\n                    .withRetryHandler(retryHandler)\n                    .build());\n        }\n\n\n        public Builder<T> setLoadBalancerCommandFactory(\n                Function<String, LoadBalancerCommand<Object>> loadBalancerCommandFactory) {\n            this.loadBalancerCommandFactory = loadBalancerCommandFactory;\n            return this;\n        }\n\n        @Override\n        protected MethodHandlerFactory buildReactiveMethodHandlerFactory() {\n            MethodHandlerFactory methodHandlerFactory = super.buildReactiveMethodHandlerFactory();\n            return hystrixEnabled\n                    ? new HystrixMethodHandlerFactory(\n\t\t\t\t\tmethodHandlerFactory,\n                    commandSetterFactory,\n                    (Function<Throwable, Object>) fallbackFactory)\n                    : methodHandlerFactory;\n        }\n\n        @Override\n        protected PublisherClientFactory buildReactiveClientFactory() {\n            PublisherClientFactory publisherClientFactory = super.buildReactiveClientFactory();\n            return methodMetadata -> {\n                PublisherHttpClient publisherClient = publisherClientFactory.apply(methodMetadata);\n                String serviceName = extractServiceName(target.url());\n                return new RibbonPublisherClient(loadBalancerCommandFactory.apply(serviceName),\n                        publisherClient, returnPublisherType(methodMetadata));\n            };\n        }\n\n        private String extractServiceName(String url){\n            try {\n                return new URI(url).getHost();\n            } catch (URISyntaxException e) {\n                throw new IllegalArgumentException(\"Can't extract service name from url\", e);\n            }\n        }\n\n        @Override\n        public Builder<T> contract(final Contract contract) {\n            super.contract(contract);\n            return this;\n        }\n\n        @Override\n        public Builder<T> requestInterceptor(ReactiveHttpRequestInterceptor requestInterceptor) {\n            super.requestInterceptor(requestInterceptor);\n            return this;\n        }\n\n        @Override\n        public Builder<T> decode404() {\n            super.decode404();\n            return this;\n        }\n\n        @Override\n        public Builder<T> statusHandler(ReactiveStatusHandler statusHandler) {\n            super.statusHandler(statusHandler);\n            return this;\n        }\n\n        @Override\n        public ReactiveFeign.Builder<T> responseMapper(\n                BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> responseMapper) {\n            super.responseMapper(responseMapper);\n            return this;\n        }\n\n        @Override\n        public Builder<T> retryWhen(ReactiveRetryPolicy retryPolicy){\n            super.retryWhen(retryPolicy);\n            return this;\n        }\n\n        @Override\n        public Builder<T> options(final ReactiveOptions options) {\n            super.options(options);\n            return this;\n        }\n    }\n\n    public interface SetterFactory {\n        HystrixObservableCommand.Setter create(Target<?> target, MethodMetadata methodMetadata);\n    }\n\n    public static class DefaultSetterFactory implements SetterFactory {\n        @Override\n        public HystrixObservableCommand.Setter create(Target<?> target, MethodMetadata methodMetadata) {\n            String groupKey = target.name();\n            String commandKey = methodMetadata.configKey();\n            return HystrixObservableCommand.Setter\n                    .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))\n                    .andCommandKey(HystrixCommandKey.Factory.asKey(commandKey));\n        }\n    }\n\n}\n"
  },
  {
    "path": "feign-reactor-cloud/src/main/java/reactivefeign/cloud/methodhandler/HystrixMethodHandler.java",
    "content": "package reactivefeign.cloud.methodhandler;\n\nimport com.netflix.hystrix.HystrixObservableCommand;\nimport feign.MethodMetadata;\nimport feign.Target;\nimport org.reactivestreams.Publisher;\nimport org.springframework.lang.Nullable;\nimport reactivefeign.cloud.CloudReactiveFeign;\nimport reactivefeign.methodhandler.MethodHandler;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport rx.Observable;\nimport rx.RxReactiveStreams;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.Arrays;\nimport java.util.function.Function;\n\nimport static feign.Feign.configKey;\nimport static feign.Util.checkNotNull;\n\n/**\n * @author Sergii Karpenko\n */\npublic class HystrixMethodHandler implements MethodHandler {\n\n    private final Method method;\n    private final Type returnPublisherType;\n    private final MethodHandler methodHandler;\n    private final Function<Throwable, Object> fallbackFactory;\n    private final HystrixObservableCommand.Setter hystrixObservableCommandSetter;\n\n    HystrixMethodHandler(\n            Target target, MethodMetadata methodMetadata,\n            MethodHandler methodHandler,\n            CloudReactiveFeign.SetterFactory setterFactory,\n            @Nullable\n                    Function<Throwable, Object> fallbackFactory) {\n        checkNotNull(target, \"target must be not null\");\n\n        checkNotNull(methodMetadata, \"methodMetadata must be not null\");\n        method = Arrays.stream(target.type().getMethods())\n                .filter(method -> configKey(target.type(), method).equals(methodMetadata.configKey()))\n                .findFirst().orElseThrow(() -> new IllegalArgumentException());\n        method.setAccessible(true);\n\n        returnPublisherType = ((ParameterizedType) methodMetadata.returnType()).getRawType();\n        this.methodHandler = checkNotNull(methodHandler, \"methodHandler must be not null\");\n        this.fallbackFactory = fallbackFactory;\n        checkNotNull(setterFactory, \"setterFactory must be not null\");\n        hystrixObservableCommandSetter = setterFactory.create(target, methodMetadata);\n    }\n\n    @Override\n    @SuppressWarnings(\"unchecked\")\n    public Publisher<Object> invoke(final Object[] argv) {\n\n        Observable<Object> observable = new HystrixObservableCommand<Object>(hystrixObservableCommandSetter) {\n            @Override\n            protected Observable<Object> construct() {\n                Publisher publisher;\n                try {\n                    publisher = (Publisher) methodHandler.invoke(argv);\n                } catch (Throwable throwable) {\n                    publisher = Mono.error(throwable);\n                }\n                return RxReactiveStreams.toObservable(publisher);\n            }\n\n            @Override\n            protected Observable<Object> resumeWithFallback() {\n                if (fallbackFactory != null) {\n                    Object fallback = fallbackFactory.apply(getExecutionException());\n                    try {\n                        Object fallbackValue = getFallbackValue(fallback, method, argv);\n                        return RxReactiveStreams.toObservable((Publisher<Object>) fallbackValue);\n                    } catch (Throwable e) {\n                        return Observable.error(e);\n                    }\n\n                } else {\n                    return super.resumeWithFallback();\n                }\n            }\n        }.toObservable();\n\n        if(returnPublisherType == Mono.class){\n            return Mono.from(RxReactiveStreams.toPublisher(observable.toSingle()));\n        } else if(returnPublisherType == Flux.class){\n            return Flux.from(RxReactiveStreams.toPublisher(observable));\n        } else {\n            throw new IllegalArgumentException(\"Unknown returnPublisherType: \" + returnPublisherType);\n        }\n    }\n\n    protected Object getFallbackValue(Object target, Method method, Object[] argv) throws Throwable {\n        return method.invoke(target, argv);\n    }\n}\n"
  },
  {
    "path": "feign-reactor-cloud/src/main/java/reactivefeign/cloud/methodhandler/HystrixMethodHandlerFactory.java",
    "content": "package reactivefeign.cloud.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport org.springframework.lang.Nullable;\nimport reactivefeign.cloud.CloudReactiveFeign;\nimport reactivefeign.methodhandler.MethodHandler;\nimport reactivefeign.methodhandler.MethodHandlerFactory;\n\nimport java.lang.reflect.Method;\nimport java.util.function.Function;\n\nimport static feign.Util.checkNotNull;\n\npublic class HystrixMethodHandlerFactory implements MethodHandlerFactory {\n\n    private final MethodHandlerFactory methodHandlerFactory;\n    private final CloudReactiveFeign.SetterFactory commandSetterFactory;\n    private final Function<Throwable, Object> fallbackFactory;\n\n    public HystrixMethodHandlerFactory(MethodHandlerFactory methodHandlerFactory,\n                                       CloudReactiveFeign.SetterFactory commandSetterFactory,\n                                       @Nullable Function<Throwable, Object> fallbackFactory) {\n        this.methodHandlerFactory = checkNotNull(methodHandlerFactory, \"methodHandlerFactory must not be null\");\n        this.commandSetterFactory = checkNotNull(commandSetterFactory, \"hystrixObservableCommandSetter must not be null\");\n        this.fallbackFactory = fallbackFactory;\n    }\n\n    @Override\n    public MethodHandler create(final Target target, final MethodMetadata metadata) {\n        return new HystrixMethodHandler(\n                target, metadata,\n                methodHandlerFactory.create(target, metadata),\n                commandSetterFactory,\n                fallbackFactory);\n    }\n\n    @Override\n    public MethodHandler createDefault(Method method) {\n        return methodHandlerFactory.createDefault(method);\n    }\n}\n"
  },
  {
    "path": "feign-reactor-cloud/src/main/java/reactivefeign/cloud/publisher/RibbonPublisherClient.java",
    "content": "package reactivefeign.cloud.publisher;\n\nimport com.netflix.loadbalancer.Server;\nimport com.netflix.loadbalancer.reactive.LoadBalancerCommand;\nimport org.reactivestreams.Publisher;\nimport org.springframework.lang.Nullable;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactivefeign.publisher.PublisherHttpClient;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport rx.Observable;\nimport rx.RxReactiveStreams;\n\nimport java.lang.reflect.Type;\nimport java.net.URI;\nimport java.net.URISyntaxException;\n\n/**\n * @author Sergii Karpenko\n */\npublic class RibbonPublisherClient implements PublisherHttpClient {\n\n    private final LoadBalancerCommand<Object> loadBalancerCommand;\n    private final PublisherHttpClient publisherClient;\n    private final Type publisherType;\n\n    public RibbonPublisherClient(@Nullable LoadBalancerCommand<Object> loadBalancerCommand,\n                                 PublisherHttpClient publisherClient,\n                                 Type publisherType) {\n        this.loadBalancerCommand = loadBalancerCommand;\n        this.publisherClient = publisherClient;\n        this.publisherType = publisherType;\n    }\n\n    @Override\n    public Publisher<?> executeRequest(ReactiveHttpRequest request) {\n\n        if (loadBalancerCommand != null) {\n            Observable<?> observable = loadBalancerCommand.submit(server -> {\n\n                ReactiveHttpRequest lbRequest = loadBalanceRequest(request, server);\n\n                Publisher<Object> publisher = (Publisher<Object>)publisherClient.executeRequest(lbRequest);\n                return RxReactiveStreams.toObservable(publisher);\n            });\n\n            Publisher<?> publisher = RxReactiveStreams.toPublisher(observable);\n\n            if(publisherType == Mono.class){\n                return Mono.from(publisher);\n            } else if(publisherType == Flux.class){\n                return Flux.from(publisher);\n            } else {\n                throw new IllegalArgumentException(\"Unknown publisherType: \" + publisherType);\n            }\n        } else {\n            return publisherClient.executeRequest(request);\n        }\n    }\n\n    protected ReactiveHttpRequest loadBalanceRequest(ReactiveHttpRequest request, Server server) {\n        URI uri = request.uri();\n        try {\n            URI lbUrl = new URI(uri.getScheme(), uri.getUserInfo(), server.getHost(), server.getPort(),\n                    uri.getPath(), uri.getQuery(), uri.getFragment());\n            return new ReactiveHttpRequest(request.method(), lbUrl, request.headers(), request.body());\n        } catch (URISyntaxException e) {\n            throw new IllegalArgumentException(e);\n        }\n    }\n}\n"
  },
  {
    "path": "feign-reactor-cloud/src/test/java/reactivefeign/cloud/HystrixReactiveHttpClientTest.java",
    "content": "package reactivefeign.cloud;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport com.netflix.hystrix.*;\nimport com.netflix.hystrix.exception.HystrixRuntimeException;\nimport feign.MethodMetadata;\nimport feign.Target;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport reactor.core.publisher.Mono;\n\nimport java.io.IOException;\nimport java.util.List;\nimport java.util.concurrent.atomic.AtomicReference;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.aResponse;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.containsString;\n\n/**\n * @author Sergii Karpenko\n */\npublic class HystrixReactiveHttpClientTest {\n\n    public static final int SLEEP_WINDOW = 100;\n    public static final int VOLUME_THRESHOLD = 1;\n    public static final String FALLBACK = \"fallback\";\n    public static final String SUCCESS = \"success!\";\n    @ClassRule\n    public static WireMockClassRule server = new WireMockClassRule(wireMockConfig().dynamicPort());\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    private static int testNo = 0;\n\n    private AtomicReference<HystrixCommandKey> lastCommandKey = new AtomicReference<>();\n\n    @Before\n    public void resetServers() {\n        server.resetAll();\n\n        testNo++;\n    }\n\n    @Test\n    public void shouldFailAsNoFallback() {\n\n        expectedException.expect(HystrixRuntimeException.class);\n        expectedException.expectMessage(containsString(\"failed and no fallback available\"));\n\n        String body = \"success!\";\n        LoadBalancingReactiveHttpClientTest.mockSuccessAfterSeveralAttempts(server, \"/\", 1, 598,\n                aResponse()\n                        .withStatus(200)\n                        .withHeader(\"Content-Type\", \"application/json\")\n                        .withBody(body));\n\n        LoadBalancingReactiveHttpClientTest.TestInterface client = CloudReactiveFeign.<LoadBalancingReactiveHttpClientTest.TestInterface>builder()\n                .setHystrixCommandSetterFactory(getSetterFactory(testNo))\n                .target(LoadBalancingReactiveHttpClientTest.TestInterface.class, \"http://localhost:\" + server.port());\n\n        client.get().block();\n    }\n\n    @Test\n    public void shouldNotFailDueToFallback() {\n\n        String body = \"success!\";\n        LoadBalancingReactiveHttpClientTest.mockSuccessAfterSeveralAttempts(server, \"/\", 1, 598,\n                aResponse()\n                        .withStatus(200)\n                        .withHeader(\"Content-Type\", \"application/json\")\n                        .withBody(body));\n\n        LoadBalancingReactiveHttpClientTest.TestInterface client = CloudReactiveFeign.<LoadBalancingReactiveHttpClientTest.TestInterface>builder()\n                .setHystrixCommandSetterFactory(getSetterFactory(testNo))\n                .setFallback(() -> Mono.just(FALLBACK))\n                .target(LoadBalancingReactiveHttpClientTest.TestInterface.class, \"http://localhost:\" + server.port());\n\n        String result = client.get().block();\n        assertThat(result).isEqualTo(FALLBACK);\n    }\n\n    @Test\n    public void shouldOpenCircuitBreakerAndCloseAfterSleepTime() throws InterruptedException {\n\n        int callsNo = VOLUME_THRESHOLD + 1;\n        LoadBalancingReactiveHttpClientTest.mockSuccessAfterSeveralAttempts(server, \"/\", VOLUME_THRESHOLD, SC_SERVICE_UNAVAILABLE,\n                aResponse()\n                        .withStatus(200)\n                        .withHeader(\"Content-Type\", \"application/json\")\n                        .withBody(SUCCESS));\n\n        LoadBalancingReactiveHttpClientTest.TestInterface client = CloudReactiveFeign.<LoadBalancingReactiveHttpClientTest.TestInterface>builder()\n                .setHystrixCommandSetterFactory(getSetterFactory(testNo))\n                .target(LoadBalancingReactiveHttpClientTest.TestInterface.class, \"http://localhost:\" + server.port());\n\n        //check that circuit breaker get opened on volume threshold\n        List<Object> results = IntStream.range(0, callsNo).mapToObj(i -> {\n            try {\n                return client.get().block();\n            } catch (Throwable t) {\n                return t;\n            }\n        }).collect(Collectors.toList());\n\n        assertThat(server.getAllServeEvents().size()).isLessThan(callsNo);\n        Throwable firstError = (Throwable) results.get(0);\n        assertThat(firstError).isInstanceOf(HystrixRuntimeException.class);\n        assertThat(firstError.getMessage())\n                .contains(\"and no fallback available\")\n                .doesNotContain(\"short-circuited\");\n        assertThat(HystrixCircuitBreaker.Factory.getInstance(lastCommandKey.get())\n                .isOpen())\n                .isTrue();\n\n        Throwable lastError = (Throwable) results.get(results.size() - 1);\n        assertThat(lastError).isInstanceOf(HystrixRuntimeException.class);\n        assertThat(lastError.getMessage())\n                .contains(\"short-circuited and no fallback available.\");\n\n        //wait to circuit breaker get closed again\n        Thread.sleep(SLEEP_WINDOW);\n\n        //check that circuit breaker get closed again\n        List<Object> resultsAfterSleep = IntStream.range(0, callsNo).mapToObj(i -> {\n            try {\n                return client.get().block();\n            } catch (Throwable t) {\n                return t;\n            }\n        }).collect(Collectors.toList());\n\n        assertThat(resultsAfterSleep).containsOnly(SUCCESS);\n        assertThat(HystrixCircuitBreaker.Factory.getInstance(lastCommandKey.get())\n                .isOpen())\n                .isFalse();\n    }\n\n    CloudReactiveFeign.SetterFactory getSetterFactory(int testNo) {\n        return new CloudReactiveFeign.SetterFactory() {\n            @Override\n            public HystrixObservableCommand.Setter create(Target<?> target, MethodMetadata methodMetadata) {\n                String groupKey = target.name();\n                HystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(methodMetadata.configKey() + testNo);\n                lastCommandKey.set(commandKey);\n                return HystrixObservableCommand.Setter\n                        .withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))\n                        .andCommandKey(commandKey)\n                        .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()\n                                .withCircuitBreakerRequestVolumeThreshold(VOLUME_THRESHOLD)\n                                .withExecutionTimeoutEnabled(false)\n                                .withCircuitBreakerSleepWindowInMilliseconds(SLEEP_WINDOW)\n                                .withMetricsHealthSnapshotIntervalInMilliseconds(10)\n                        );\n            }\n        };\n    }\n\n}\n"
  },
  {
    "path": "feign-reactor-cloud/src/test/java/reactivefeign/cloud/LoadBalancingReactiveHttpClientTest.java",
    "content": "package reactivefeign.cloud;\n\nimport com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport com.netflix.client.*;\nimport com.netflix.client.config.CommonClientConfigKey;\nimport com.netflix.client.config.DefaultClientConfigImpl;\nimport com.netflix.hystrix.HystrixCommandGroupKey;\nimport com.netflix.hystrix.HystrixCommandKey;\nimport com.netflix.hystrix.HystrixCommandProperties;\nimport com.netflix.hystrix.HystrixObservableCommand;\nimport com.netflix.loadbalancer.BaseLoadBalancer;\nimport com.netflix.loadbalancer.ILoadBalancer;\nimport com.netflix.loadbalancer.Server;\nimport feign.RequestLine;\nimport feign.RetryableException;\nimport org.junit.*;\nimport org.junit.rules.ExpectedException;\nimport reactivefeign.publisher.RetryPublisherHttpClient;\nimport reactor.core.publisher.Mono;\n\nimport java.util.stream.Stream;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;\nimport static java.util.Arrays.asList;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.hamcrest.Matchers.*;\nimport static reactivefeign.ReactiveRetryers.retry;\n\n/**\n * @author Sergii Karpenko\n */\npublic class LoadBalancingReactiveHttpClientTest {\n\n    @ClassRule\n    public static WireMockClassRule server1 = new WireMockClassRule(wireMockConfig().dynamicPort());\n    @ClassRule\n    public static WireMockClassRule server2 = new WireMockClassRule(wireMockConfig().dynamicPort());\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    private static String serviceName = \"LoadBalancingTargetTest-loadBalancingDefaultPolicyRoundRobin\";\n\n    @BeforeClass\n    public static void setupServersList() throws ClientException {\n        DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl();\n        clientConfig.loadDefaultValues();\n        clientConfig.setProperty(CommonClientConfigKey.NFLoadBalancerClassName, BaseLoadBalancer.class.getName());\n        ILoadBalancer lb = ClientFactory.registerNamedLoadBalancerFromclientConfig(serviceName, clientConfig);\n        lb.addServers(asList(new Server(\"localhost\", server1.port()), new Server(\"localhost\", server2.port())));\n    }\n\n    @Before\n    public void resetServers() {\n        server1.resetAll();\n        server2.resetAll();\n    }\n\n    @Test\n    public void shouldLoadBalanceRequests() {\n        String body = \"success!\";\n        mockSuccess(server1, body);\n        mockSuccess(server2, body);\n\n        TestInterface client = CloudReactiveFeign.<TestInterface>builder()\n                .enableLoadBalancer()\n                .setHystrixCommandSetterFactory(getSetterFactoryWithTimeoutDisabled())\n                .target(TestInterface.class, \"http://\" + serviceName);\n\n        String result1 = client.get().block();\n        String result2 = client.get().block();\n\n        assertThat(result1)\n                .isEqualTo(result2)\n                .isEqualTo(body);\n\n        server1.verify(1, getRequestedFor(urlEqualTo(\"/\")));\n        server2.verify(1, getRequestedFor(urlEqualTo(\"/\")));\n    }\n\n    @Test\n    public void shouldFailAsPolicyWoRetries() {\n\n        expectedException.expect(RuntimeException.class);\n        expectedException.expectCause(allOf(isA(RetryPublisherHttpClient.OutOfRetriesException.class),\n                hasProperty(\"cause\", isA(RetryableException.class))));\n\n        try {\n            loadBalancingWithRetry(2, 0, 0);\n        } catch (Throwable t) {\n            assertThat(server1.getAllServeEvents().size() == 1\n                    ^ server2.getAllServeEvents().size() == 1);\n            throw t;\n        }\n    }\n\n    @Test\n    public void shouldRetryOnSameAndFail() {\n\n        expectedException.expect(RuntimeException.class);\n        expectedException.expectCause(allOf(isA(RetryPublisherHttpClient.OutOfRetriesException.class),\n                                            hasProperty(\"cause\", isA(RetryableException.class))));\n\n        try {\n            loadBalancingWithRetry(2, 1, 0);\n        } catch (Throwable t) {\n            assertThat(server1.getAllServeEvents().size() == 2\n                    ^ server2.getAllServeEvents().size() == 2);\n            throw t;\n        }\n    }\n\n    @Test\n    public void shouldRetryOnNextAndFail() {\n\n        expectedException.expect(RuntimeException.class);\n        expectedException.expectCause(isA(ClientException.class));\n\n        try {\n            loadBalancingWithRetry(2, 1, 1);\n        } catch (Throwable t) {\n            assertThat(server1.getAllServeEvents().size() == 2\n                    && server2.getAllServeEvents().size() == 2);\n            throw t;\n        }\n    }\n\n    @Test\n    public void shouldRetryOnSameAndSuccess() {\n\n        loadBalancingWithRetry(2, 2, 0);\n\n        assertThat(server1.getAllServeEvents().size() == 3\n                ^ server2.getAllServeEvents().size() == 3);\n\n    }\n\n    private void loadBalancingWithRetry(int failedAttemptsNo, int retryOnSame, int retryOnNext) {\n        String body = \"success!\";\n        Stream.of(server1, server2).forEach(server -> {\n            mockSuccessAfterSeveralAttempts(server, \"/\",\n                    failedAttemptsNo, 503,\n                    aResponse()\n                            .withStatus(200)\n                            .withHeader(\"Content-Type\", \"application/json\")\n                            .withBody(body));\n        });\n\n        RetryHandler retryHandler = new RequestSpecificRetryHandler(true, true,\n                new DefaultLoadBalancerRetryHandler(0, retryOnNext, true), null);\n\n        TestInterface client = CloudReactiveFeign.<TestInterface>builder()\n                .retryWhen(retry(retryOnSame))\n                .enableLoadBalancer(retryHandler)\n                .setHystrixCommandSetterFactory(getSetterFactoryWithTimeoutDisabled())\n                .target(TestInterface.class, \"http://\" + serviceName);\n\n        String result = client.get().block();\n        assertThat(result).isEqualTo(body);\n    }\n\n    private CloudReactiveFeign.SetterFactory getSetterFactoryWithTimeoutDisabled() {\n        return (target, methodMetadata) -> {\n\t\t\tString groupKey = target.name();\n\t\t\tHystrixCommandKey commandKey = HystrixCommandKey.Factory.asKey(methodMetadata.configKey());\n\t\t\treturn HystrixObservableCommand.Setter\n\t\t\t\t\t.withGroupKey(HystrixCommandGroupKey.Factory.asKey(groupKey))\n\t\t\t\t\t.andCommandKey(commandKey)\n\t\t\t\t\t.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()\n\t\t\t\t\t\t\t.withExecutionTimeoutEnabled(false)\n\t\t\t\t\t);\n\t\t};\n    }\n\n    static void mockSuccess(WireMockClassRule server, String body) {\n        server.stubFor(get(urlEqualTo(\"/\"))\n                .willReturn(aResponse()\n                        .withStatus(200)\n                        .withHeader(\"Content-Type\", \"application/json\")\n                        .withBody(body)));\n    }\n\n    static void mockSuccessAfterSeveralAttempts(WireMockClassRule server, String url,\n                                                int failedAttemptsNo, int errorCode, ResponseDefinitionBuilder response) {\n        String state = STARTED;\n        for (int attempt = 0; attempt < failedAttemptsNo; attempt++) {\n            String nextState = \"attempt\" + attempt;\n            server.stubFor(get(urlEqualTo(url))\n                    .inScenario(\"testScenario\")\n                    .whenScenarioStateIs(state)\n                    .willReturn(aResponse()\n                            .withStatus(errorCode)\n                            .withHeader(\"Retry-After\", \"1\"))\n                    .willSetStateTo(nextState));\n\n            state = nextState;\n        }\n\n        server.stubFor(get(urlEqualTo(url))\n                .inScenario(\"testScenario\")\n                .whenScenarioStateIs(state)\n                .willReturn(response));\n    }\n\n\n    interface TestInterface {\n\n        @RequestLine(\"GET /\")\n        Mono<String> get();\n    }\n}\n"
  },
  {
    "path": "feign-reactor-core/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n\n    Copyright 2018 The Feign Authors\n\n    Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n    in compliance with the License. You may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\n    Unless required by applicable law or agreed to in writing, software distributed under the License\n    is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n    or implied. See the License for the specific language governing permissions and limitations under\n    the License.\n\n-->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n         xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.github.reactivefeign</groupId>\n        <artifactId>feign-reactor</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>feign-reactor-core</artifactId>\n    <packaging>jar</packaging>\n    <name>Feign Reactive Core</name>\n\n    <properties>\n        <main.basedir>${project.basedir}/..</main.basedir>\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>io.projectreactor</groupId>\n            <artifactId>reactor-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.github.openfeign</groupId>\n            <artifactId>feign-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.slf4j</groupId>\n            <artifactId>slf4j-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>commons-httpclient</groupId>\n            <artifactId>commons-httpclient</artifactId>\n        </dependency>\n\n        <!-- Tests -->\n        <dependency>\n            <groupId>io.projectreactor</groupId>\n            <artifactId>reactor-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.tomakehurst</groupId>\n            <artifactId>wiremock</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.fasterxml.jackson.datatype</groupId>\n            <artifactId>jackson-datatype-jsr310</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.hamcrest</groupId>\n            <artifactId>hamcrest-library</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-all</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.awaitility</groupId>\n            <artifactId>awaitility</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.logging.log4j</groupId>\n            <artifactId>log4j-slf4j-impl</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <exclusions>\n                <exclusion>\n                    <artifactId>spring-boot-starter-logging</artifactId>\n                    <groupId>org.springframework.boot</groupId>\n                </exclusion>\n            </exclusions>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-jar-plugin</artifactId>\n                <version>3.1.0</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>test-jar</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveContract.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport feign.Contract;\nimport feign.MethodMetadata;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport static feign.Util.checkNotNull;\nimport static reactivefeign.utils.FeignUtils.bodyActualType;\nimport static reactivefeign.utils.FeignUtils.returnActualType;\n\n/**\n * Contract allowing only {@link Mono} and {@link Flux} return type.\n *\n * @author Sergii Karpenko\n */\npublic class ReactiveContract implements Contract {\n\n  private final Contract delegate;\n\n  public ReactiveContract(final Contract delegate) {\n    this.delegate = checkNotNull(delegate, \"delegate must not be null\");\n  }\n\n  @Override\n  public List<MethodMetadata> parseAndValidatateMetadata(final Class<?> targetType) {\n    final List<MethodMetadata> methodsMetadata =\n        this.delegate.parseAndValidatateMetadata(targetType);\n\n    for (final MethodMetadata metadata : methodsMetadata) {\n      final Type type = metadata.returnType();\n      if (!isReactorType(type)) {\n        throw new IllegalArgumentException(String.format(\n            \"Method %s of contract %s doesn't returns reactor.core.publisher.Mono or reactor.core.publisher.Flux\",\n            metadata.configKey(), targetType.getSimpleName()));\n      }\n\n      if(returnActualType(metadata) == byte[].class || bodyActualType(metadata) == byte[].class){\n        throw new IllegalArgumentException(String.format(\n                \"Method %s of contract %s will cause data to be copied, use ByteBuffer instead\",\n                metadata.configKey(), targetType.getSimpleName()));\n      }\n    }\n\n    return methodsMetadata;\n  }\n\n  private boolean isReactorType(final Type type) {\n    return (type instanceof ParameterizedType)\n        && (((ParameterizedType) type).getRawType() == Mono.class\n            || ((ParameterizedType) type).getRawType() == Flux.class);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveFeign.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport feign.*;\nimport feign.codec.ErrorDecoder;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveHttpRequestInterceptor;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactivefeign.client.statushandler.ReactiveStatusHandler;\nimport reactivefeign.client.statushandler.ReactiveStatusHandlers;\nimport reactivefeign.methodhandler.MethodHandler;\nimport reactivefeign.methodhandler.DefaultMethodHandler;\nimport reactivefeign.methodhandler.MethodHandlerFactory;\nimport reactivefeign.methodhandler.ReactiveMethodHandlerFactory;\nimport reactivefeign.publisher.*;\nimport reactivefeign.utils.Pair;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.lang.reflect.Type;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.function.BiFunction;\nimport java.util.function.Function;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static feign.Util.checkNotNull;\nimport static feign.Util.isDefault;\nimport static reactivefeign.client.InterceptorReactiveHttpClient.intercept;\nimport static reactivefeign.client.LoggerReactiveHttpClient.log;\nimport static reactivefeign.client.ResponseMappers.ignore404;\nimport static reactivefeign.client.ResponseMappers.mapResponse;\nimport static reactivefeign.client.StatusHandlerReactiveHttpClient.handleStatus;\nimport static reactivefeign.utils.FeignUtils.returnPublisherType;\nimport static reactivefeign.utils.MultiValueMapUtils.addOrdered;\n\n/**\n * Allows Feign interfaces to accept {@link Publisher} as body and return reactive {@link Mono} or\n * {@link Flux}.\n *\n * @author Sergii Karpenko\n */\npublic class ReactiveFeign {\n\n  private final ParseHandlersByName targetToHandlersByName;\n  private final InvocationHandlerFactory factory;\n\n  protected ReactiveFeign(\n      final ParseHandlersByName targetToHandlersByName,\n      final InvocationHandlerFactory factory) {\n    this.targetToHandlersByName = targetToHandlersByName;\n    this.factory = factory;\n  }\n\n  @SuppressWarnings(\"unchecked\")\n  public <T> T newInstance(Target<T> target) {\n    final Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);\n    final Map<Method, InvocationHandlerFactory.MethodHandler> methodToHandler = new LinkedHashMap<>();\n    final List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<>();\n\n    for (final Method method : target.type().getMethods()) {\n      if (isDefault(method)) {\n        final DefaultMethodHandler handler = new DefaultMethodHandler(method);\n        defaultMethodHandlers.add(handler);\n        methodToHandler.put(method, handler);\n      } else {\n        methodToHandler.put(method,\n                nameToHandler.get(Feign.configKey(target.type(), method)));\n      }\n    }\n\n    final InvocationHandler handler = factory.create(target, methodToHandler);\n    T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(),\n            new Class<?>[] {target.type()}, handler);\n\n    for (final DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {\n      defaultMethodHandler.bindTo(proxy);\n    }\n\n    return proxy;\n  }\n\n  /**\n   * ReactiveFeign builder.\n   */\n  public abstract static class Builder<T> {\n    protected Contract contract;\n    protected Function<MethodMetadata, ReactiveHttpClient> clientFactory;\n    protected ReactiveHttpRequestInterceptor requestInterceptor;\n    protected BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> responseMapper;\n    protected ReactiveStatusHandler statusHandler =\n        ReactiveStatusHandlers.defaultFeign(new ErrorDecoder.Default());\n    protected InvocationHandlerFactory invocationHandlerFactory =\n        new ReactiveInvocationHandler.Factory();\n    protected boolean decode404 = false;\n    protected Target<T> target;\n\n    private Function<Flux<Throwable>, Flux<Throwable>> retryFunction;\n\n    protected Builder(){\n      contract(new Contract.Default());\n    }\n\n    abstract public Builder<T> options(ReactiveOptions options);\n\n    protected Builder<T> clientFactory(Function<MethodMetadata, ReactiveHttpClient> clientFactory) {\n      this.clientFactory = clientFactory;\n      return this;\n    }\n\n    /**\n     * Sets contract. Provided contract will be wrapped in {@link ReactiveContract}\n     *\n     * @param contract contract.\n     * @return this builder\n     */\n    public Builder<T> contract(final Contract contract) {\n      this.contract = new ReactiveContract(contract);\n      return this;\n    }\n\n    public Builder<T> addHeaders(List<Pair<String, String>> headers) {\n      this.requestInterceptor = request -> {\n        headers.forEach(header -> addOrdered(request.headers(), header.left, header.right));\n        return request;\n      };\n      return this;\n    }\n\n    public Builder<T> requestInterceptor(ReactiveHttpRequestInterceptor requestInterceptor) {\n      this.requestInterceptor = requestInterceptor;\n      return this;\n    }\n\n    /**\n     * This flag indicates that the reactive feign client should process responses with 404 status,\n     * specifically returning empty {@link Mono} or {@link Flux} instead of throwing\n     * {@link FeignException}.\n     * <p>\n     * <p>\n     * This flag only works with 404, as opposed to all or arbitrary status codes. This was an\n     * explicit decision: 404 - empty is safe, common and doesn't complicate redirection, retry or\n     * fallback policy.\n     *\n     * @return this builder\n     */\n    public Builder<T> decode404() {\n      this.decode404 = true;\n      return this;\n    }\n\n    public Builder<T> statusHandler(ReactiveStatusHandler statusHandler) {\n      this.statusHandler = statusHandler;\n      return this;\n    }\n\n    /**\n     * The most common way to introduce custom logic on handling http response\n     *\n     * @param responseMapper\n     * @return\n     */\n    public Builder<T> responseMapper(BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> responseMapper) {\n      this.responseMapper = responseMapper;\n      return this;\n    }\n\n    public Builder<T> retryWhen(Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {\n      this.retryFunction = retryFunction;\n      return this;\n    }\n\n    public Builder<T> retryWhen(ReactiveRetryPolicy retryPolicy) {\n      return retryWhen(retryPolicy.toRetryFunction());\n    }\n\n    /**\n     * Defines target and builds client.\n     *\n     * @param apiType API interface\n     * @param url base URL\n     * @return built client\n     */\n    public T target(final Class<T> apiType, final String url) {\n      return target(new Target.HardCodedTarget<>(apiType, url));\n    }\n\n    /**\n     * Defines target and builds client.\n     *\n     * @param target target instance\n     * @return built client\n     */\n    public T target(final Target<T> target) {\n      this.target = target;\n      return build().newInstance(target);\n    }\n\n    protected ReactiveFeign build() {\n      final ParseHandlersByName handlersByName = new ParseHandlersByName(\n              contract, buildReactiveMethodHandlerFactory());\n      return new ReactiveFeign(handlersByName, invocationHandlerFactory);\n    }\n\n    protected MethodHandlerFactory buildReactiveMethodHandlerFactory() {\n      return new ReactiveMethodHandlerFactory(buildReactiveClientFactory());\n    }\n\n    protected PublisherClientFactory buildReactiveClientFactory() {\n      return methodMetadata -> {\n\n        checkNotNull(clientFactory,\n                \"clientFactory wasn't provided in ReactiveFeign builder\");\n\n        ReactiveHttpClient reactiveClient = clientFactory.apply(methodMetadata);\n\n        if (requestInterceptor != null) {\n          reactiveClient = intercept(reactiveClient, requestInterceptor);\n        }\n\n        reactiveClient = log(reactiveClient, methodMetadata);\n\n        if (responseMapper != null) {\n          reactiveClient = mapResponse(reactiveClient, methodMetadata, responseMapper);\n        }\n\n        if (decode404) {\n          reactiveClient = mapResponse(reactiveClient, methodMetadata, ignore404());\n        }\n\n        if (statusHandler != null) {\n          reactiveClient = handleStatus(reactiveClient, methodMetadata, statusHandler);\n        }\n\n        reactivefeign.publisher.PublisherHttpClient publisherClient = toPublisher(reactiveClient, methodMetadata);\n        if (retryFunction != null) {\n          publisherClient = retry(publisherClient, methodMetadata, retryFunction);\n        }\n\n        return publisherClient;\n      };\n    }\n\n    protected PublisherHttpClient retry(\n            PublisherHttpClient publisherClient,\n            MethodMetadata methodMetadata,\n            Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {\n      Type returnPublisherType = returnPublisherType(methodMetadata);\n      if(returnPublisherType == Mono.class){\n        return new MonoRetryPublisherHttpClient(\n                (MonoPublisherHttpClient)publisherClient, methodMetadata, retryFunction);\n      } else if(returnPublisherType == Flux.class) {\n        return new FluxRetryPublisherHttpClient(\n                (FluxPublisherHttpClient)publisherClient, methodMetadata, retryFunction);\n      } else {\n        throw new IllegalArgumentException(\"Unknown returnPublisherType: \" + returnPublisherType);\n      }\n    }\n\n    protected PublisherHttpClient toPublisher(ReactiveHttpClient reactiveHttpClient, MethodMetadata methodMetadata){\n      Type returnPublisherType = returnPublisherType(methodMetadata);\n      if(returnPublisherType == Mono.class){\n        return new MonoPublisherHttpClient(reactiveHttpClient);\n      } else if(returnPublisherType == Flux.class){\n        return new FluxPublisherHttpClient(reactiveHttpClient);\n      } else {\n        throw new IllegalArgumentException(\"Unknown returnPublisherType: \" + returnPublisherType);\n      }\n    }\n  }\n\n  public static final class ParseHandlersByName {\n    private final Contract contract;\n    private final MethodHandlerFactory factory;\n\n    ParseHandlersByName(final Contract contract, final MethodHandlerFactory factory) {\n      this.contract = contract;\n      this.factory = factory;\n    }\n\n    Map<String, MethodHandler> apply(final Target target) {\n      Map<String, MethodMetadata> metadata = contract.parseAndValidatateMetadata(target.type())\n              .stream()\n              .collect(Collectors.toMap(\n                      MethodMetadata::configKey,\n                      md -> md\n              ));\n      Map<String, Method> configKeyToMethod = Stream.of(target.type().getMethods())\n              .collect(Collectors.toMap(\n                      method -> Feign.configKey(target.type(), method),\n                      method -> method\n              ));\n\n      final Map<String, MethodHandler> result = new LinkedHashMap<>();\n\n      for (final Map.Entry<String, Method> entry : configKeyToMethod.entrySet()) {\n        String configKey = entry.getKey();\n        MethodMetadata md = metadata.get(configKey);\n        MethodHandler methodHandler = md != null\n                ? factory.create(target, md)\n                : factory.createDefault(entry.getValue());  //isDefault(entry.getValue())\n        result.put(configKey, methodHandler);\n      }\n\n      return result;\n    }\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveInvocationHandler.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport feign.InvocationHandlerFactory;\nimport feign.InvocationHandlerFactory.MethodHandler;\nimport feign.Target;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.InvocationHandler;\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Proxy;\nimport java.util.Map;\n\nimport static feign.Util.checkNotNull;\n\n/**\n * {@link InvocationHandler} implementation that transforms calls to methods of feign contract into\n * asynchronous HTTP requests via spring WebClient.\n *\n * @author Sergii Karpenko\n */\npublic final class ReactiveInvocationHandler implements InvocationHandler {\n  private final Target<?> target;\n  private final Map<Method, MethodHandler> dispatch;\n\n  private ReactiveInvocationHandler(final Target<?> target,\n      final Map<Method, MethodHandler> dispatch) {\n    this.target = checkNotNull(target, \"target must not be null\");\n    this.dispatch = checkNotNull(dispatch, \"dispatch must not be null\");\n    defineObjectMethodsHandlers();\n  }\n\n  private void defineObjectMethodsHandlers() {\n    try {\n      dispatch.put(Object.class.getMethod(\"equals\", Object.class),\n          args -> {\n            Object otherHandler = args.length > 0 && args[0] != null\n                ? Proxy.getInvocationHandler(args[0])\n                : null;\n            return equals(otherHandler);\n          });\n      dispatch.put(Object.class.getMethod(\"hashCode\"),\n          args -> hashCode());\n      dispatch.put(Object.class.getMethod(\"toString\"),\n          args -> toString());\n    } catch (NoSuchMethodException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  @Override\n  public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {\n    return dispatch.get(method).invoke(args);\n  }\n\n  @Override\n  public boolean equals(final Object other) {\n    if (other instanceof ReactiveInvocationHandler) {\n      final ReactiveInvocationHandler otherHandler = (ReactiveInvocationHandler) other;\n      return this.target.equals(otherHandler.target);\n    }\n    return false;\n  }\n\n  @Override\n  public int hashCode() {\n    return target.hashCode();\n  }\n\n  @Override\n  public String toString() {\n    return target.toString();\n  }\n\n  /**\n   * Factory for ReactiveInvocationHandler.\n   */\n  public static final class Factory implements InvocationHandlerFactory {\n\n    @Override\n    public InvocationHandler create(final Target target,\n                                    final Map<Method, MethodHandler> dispatch) {\n      return new ReactiveInvocationHandler(target, dispatch);\n    }\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveOptions.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ReactiveOptions {\n\n  private final Long connectTimeoutMillis;\n  private final Long readTimeoutMillis;\n  private final Boolean tryUseCompression;\n\n  private ReactiveOptions(Long connectTimeoutMillis, Long readTimeoutMillis,\n      Boolean tryUseCompression) {\n    this.connectTimeoutMillis = connectTimeoutMillis;\n    this.readTimeoutMillis = readTimeoutMillis;\n    this.tryUseCompression = tryUseCompression;\n  }\n\n  public Long getConnectTimeoutMillis() {\n    return connectTimeoutMillis;\n  }\n\n  public Long getReadTimeoutMillis() {\n    return readTimeoutMillis;\n  }\n\n  public Boolean isTryUseCompression() {\n    return tryUseCompression;\n  }\n\n  public boolean isEmpty() {\n    return connectTimeoutMillis == null && readTimeoutMillis == null\n        && tryUseCompression == null;\n  }\n\n  public static class Builder {\n    private Long connectTimeoutMillis;\n    private Long readTimeoutMillis;\n    private Boolean tryUseCompression;\n\n    public Builder() {}\n\n    public Builder setConnectTimeoutMillis(long connectTimeoutMillis) {\n      this.connectTimeoutMillis = connectTimeoutMillis;\n      return this;\n    }\n\n    public Builder setReadTimeoutMillis(long readTimeoutMillis) {\n      this.readTimeoutMillis = readTimeoutMillis;\n      return this;\n    }\n\n    public Builder setTryUseCompression(boolean tryUseCompression) {\n      this.tryUseCompression = tryUseCompression;\n      return this;\n    }\n\n    public ReactiveOptions build() {\n      return new ReactiveOptions(connectTimeoutMillis, readTimeoutMillis,\n          tryUseCompression);\n    }\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveRetryPolicy.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport reactor.core.Exceptions;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.util.function.Tuples;\n\nimport java.time.Duration;\nimport java.util.function.Function;\n\npublic interface ReactiveRetryPolicy {\n  /**\n   * @param error\n   * @param attemptNo\n   * @return -1 if should not be retried, 0 if retry immediately\n   */\n  long retryDelay(Throwable error, int attemptNo);\n\n  default Function<Flux<Throwable>, Flux<Throwable>> toRetryFunction() {\n    return errors -> errors\n        .zipWith(Flux.range(1, Integer.MAX_VALUE), (error, index) -> {\n          long delay = retryDelay(error, index);\n          if (delay >= 0) {\n            return Tuples.of(delay, error);\n          } else {\n            throw Exceptions.propagate(error);\n          }\n        }).flatMap(\n            tuple2 -> tuple2.getT1() > 0\n                ? Mono.delay(Duration.ofMillis(tuple2.getT1()))\n                    .map(time -> tuple2.getT2())\n                : Mono.just(tuple2.getT2()));\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/ReactiveRetryers.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport feign.RetryableException;\n\nimport java.util.Date;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ReactiveRetryers {\n\n  public static ReactiveRetryPolicy retry(int maxRetries) {\n    return (error, attemptNo) -> attemptNo <= maxRetries ? 0 : -1;\n  }\n\n  public static ReactiveRetryPolicy retryWithBackoff(int maxRetries, long periodInMs) {\n    return (error, attemptNo) -> {\n      if (attemptNo <= maxRetries) {\n        long delay;\n        Date retryAfter;\n        // \"Retry-After\" header set\n        if (error instanceof RetryableException\n            && (retryAfter = ((RetryableException) error)\n                .retryAfter()) != null) {\n          delay = retryAfter.getTime() - System.currentTimeMillis();\n          delay = Math.min(delay, periodInMs);\n          delay = Math.max(delay, 0);\n        } else {\n          delay = periodInMs;\n        }\n        return delay;\n      } else {\n        return -1;\n      }\n    };\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/DelegatingReactiveHttpResponse.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @author Sergii Karpenko\n */\nabstract public class DelegatingReactiveHttpResponse implements ReactiveHttpResponse {\n\n  private final ReactiveHttpResponse response;\n\n  protected DelegatingReactiveHttpResponse(ReactiveHttpResponse response) {\n    this.response = response;\n  }\n\n  protected ReactiveHttpResponse getResponse() {\n    return response;\n  }\n\n  @Override\n  public int status() {\n    return response.status();\n  }\n\n  @Override\n  public Map<String, List<String>> headers() {\n    return response.headers();\n  }\n\n  @Override\n  public Mono<byte[]> bodyData() {\n    throw new UnsupportedOperationException();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/InterceptorReactiveHttpClient.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\n/**\n * Used to modify request before call. May be used to set auth headers to all requests.\n *\n * @author Sergii Karpenko\n */\npublic class InterceptorReactiveHttpClient {\n\n  public static ReactiveHttpClient intercept(ReactiveHttpClient reactiveHttpClient,\n\t\t\t\t\t\t\t\t\t\t\t  ReactiveHttpRequestInterceptor interceptor) {\n    return request -> reactiveHttpClient.executeRequest(interceptor.apply(request));\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/LoggerReactiveHttpClient.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\nimport feign.MethodMetadata;\nimport org.reactivestreams.Publisher;\nimport org.slf4j.LoggerFactory;\nimport reactivefeign.utils.Pair;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.concurrent.atomic.AtomicLong;\nimport java.util.function.Consumer;\nimport java.util.function.Supplier;\nimport java.util.stream.Collectors;\n\nimport static reactivefeign.utils.FeignUtils.methodTag;\nimport static reactor.core.publisher.Mono.just;\n\n/**\n * Wraps {@link ReactiveHttpClient} with log logic\n *\n * @author Sergii Karpenko\n */\npublic class LoggerReactiveHttpClient implements ReactiveHttpClient {\n\n  private final org.slf4j.Logger logger = LoggerFactory.getLogger(LoggerReactiveHttpClient.class);\n\n  private final ReactiveHttpClient reactiveClient;\n  private final String methodTag;\n\n  public static ReactiveHttpClient log(ReactiveHttpClient reactiveClient, MethodMetadata methodMetadata) {\n    return new LoggerReactiveHttpClient(reactiveClient, methodMetadata);\n  }\n\n  private LoggerReactiveHttpClient(ReactiveHttpClient reactiveClient,\n      MethodMetadata methodMetadata) {\n    this.reactiveClient = reactiveClient;\n    this.methodTag = methodTag(methodMetadata);\n  }\n\n  @Override\n  public Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request) {\n\n    AtomicLong start = new AtomicLong(-1);\n    return Mono\n        .defer(() -> {\n          start.set(System.currentTimeMillis());\n          return just(request);\n        })\n        .flatMap(req -> {\n          req = logRequest(methodTag, req);\n\n          return reactiveClient.executeRequest(req)\n              .doOnNext(resp -> logResponseHeaders(methodTag, resp,\n                  System.currentTimeMillis() - start.get()));\n        })\n        .map(resp -> new LoggerReactiveHttpResponse(resp, start));\n  }\n\n  private ReactiveHttpRequest logRequest(\n          String feignMethodTag, ReactiveHttpRequest request) {\n    if (logger.isDebugEnabled()) {\n      logger.debug(\"[{}]--->{} {} HTTP/1.1\", feignMethodTag, request.method(),\n          request.uri());\n    }\n\n    if (logger.isTraceEnabled()) {\n      logger.trace(\"[{}] REQUEST HEADERS\\n{}\", feignMethodTag,\n          msg(() -> request.headers().entrySet().stream()\n              .map(entry -> String.format(\"%s:%s\", entry.getKey(),\n                  entry.getValue()))\n              .collect(Collectors.joining(\"\\n\"))));\n\n      if(request.body() != null) {\n        Publisher<Object> bodyLogged;\n        if (request.body() instanceof Mono) {\n          bodyLogged = ((Mono<Object>) request.body()).doOnNext(body -> logger.trace(\n                  \"[{}] REQUEST BODY\\n{}\", feignMethodTag, body));\n        } else if (request.body() instanceof Flux) {\n          bodyLogged = ((Flux<Object>) request.body()).doOnNext(body -> logger.trace(\n                  \"[{}] REQUEST BODY ELEMENT\\n{}\", feignMethodTag, body));\n        } else {\n          throw new IllegalArgumentException(\"Unsupported publisher type: \" + request.body().getClass());\n        }\n        return new ReactiveHttpRequest(request, bodyLogged);\n      }\n    }\n\n    return request;\n  }\n\n  private void logResponseHeaders(String feignMethodTag,\n                                  ReactiveHttpResponse httpResponse,\n                                  long elapsedTime) {\n    if (logger.isTraceEnabled()) {\n      logger.trace(\"[{}] RESPONSE HEADERS\\n{}\", feignMethodTag,\n          msg(() -> httpResponse.headers().entrySet().stream()\n              .flatMap(entry -> entry.getValue().stream()\n                  .map(value -> new Pair<>(entry.getKey(), value)))\n              .map(pair -> String.format(\"%s:%s\", pair.left, pair.right))\n              .collect(Collectors.joining(\"\\n\"))));\n    }\n    if (logger.isDebugEnabled()) {\n      logger.debug(\"[{}]<--- headers takes {} milliseconds\", feignMethodTag,\n          elapsedTime);\n    }\n  }\n\n  private void logResponseBodyAndTime(String feignMethodTag, Object response, long elapsedTime) {\n    if (logger.isTraceEnabled()) {\n      logger.trace(\"[{}] RESPONSE BODY\\n{}\", feignMethodTag, response);\n    }\n\n    if (logger.isDebugEnabled()) {\n      logger.debug(\"[{}]<--- body takes {} milliseconds\", feignMethodTag, elapsedTime);\n    }\n  }\n\n  private class LoggerReactiveHttpResponse extends DelegatingReactiveHttpResponse {\n\n    private final AtomicLong start;\n\n    private LoggerReactiveHttpResponse(ReactiveHttpResponse response, AtomicLong start) {\n      super(response);\n      this.start = start;\n    }\n\n    @Override\n    public Publisher<?> body() {\n      Publisher<?> publisher = getResponse().body();\n\n      if (publisher instanceof Mono) {\n        return ((Mono<?>) publisher).doOnNext(responseBodyLogger(start));\n      } else {\n        return ((Flux<?>) publisher).doOnNext(responseBodyLogger(start));\n      }\n\n    }\n\n    @Override\n    public Mono<byte[]> bodyData() {\n      Mono<byte[]> publisher = getResponse().bodyData();\n\n      return publisher.doOnNext(responseBodyLogger(start));\n    }\n\n    private Consumer<Object> responseBodyLogger(AtomicLong start) {\n      return result -> logResponseBodyAndTime(methodTag, result,\n          System.currentTimeMillis() - start.get());\n    }\n  }\n\n  private static MessageSupplier msg(Supplier<?> supplier) {\n    return new MessageSupplier(supplier);\n  }\n\n  static class MessageSupplier {\n    private Supplier<?> supplier;\n\n    public MessageSupplier(Supplier<?> supplier) {\n      this.supplier = supplier;\n    }\n\n    @Override\n    public String toString() {\n      return supplier.get().toString();\n    }\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpClient.java",
    "content": "package reactivefeign.client;\n\nimport reactor.core.publisher.Mono;\n\npublic interface ReactiveHttpClient {\n\n\tMono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request);\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpRequest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\nimport org.reactivestreams.Publisher;\n\nimport java.net.URI;\nimport java.util.List;\nimport java.util.Map;\n\nimport static feign.Util.checkNotNull;\n\n/**\n * An immutable reactive request to an http server.\n * \n * @author Sergii Karpenko\n */\npublic final class ReactiveHttpRequest {\n\n  private final String method;\n  private final URI uri;\n  private final Map<String, List<String>> headers;\n  private final Publisher<Object> body;\n\n  /**\n   * No parameters can be null except {@code body}. All parameters must be effectively immutable,\n   * via safe copies, not mutating or otherwise.\n   */\n  public ReactiveHttpRequest(String method, URI uri,\n      Map<String, List<String>> headers, Publisher<Object> body) {\n    this.method = checkNotNull(method, \"method of %s\", uri);\n    this.uri = checkNotNull(uri, \"url\");\n    this.headers = checkNotNull(headers, \"headers of %s %s\", method, uri);\n    this.body = body; // nullable\n  }\n\n  public ReactiveHttpRequest(ReactiveHttpRequest request, Publisher<Object> body){\n     this(request.method, request.uri, request.headers, body);\n  }\n\n  /* Method to invoke on the server. */\n  public String method() {\n    return method;\n  }\n\n  /* Fully resolved URL including query. */\n  public URI uri() {\n    return uri;\n  }\n\n  /* Ordered list of headers that will be sent to the server. */\n  public Map<String, List<String>> headers() {\n    return headers;\n  }\n\n  /**\n   * If present, this is the replayable body to send to the server.\n   */\n  public Publisher<Object> body() {\n    return body;\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpRequestInterceptor.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\nimport java.util.function.Function;\n\n/**\n * Used to modify request before call. May be used to set auth headers to all requests.\n *\n * @author Sergii Karpenko\n *\n */\npublic interface ReactiveHttpRequestInterceptor\n    extends Function<ReactiveHttpRequest, ReactiveHttpRequest> {\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReactiveHttpResponse.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * Reactive response from an http server.\n * \n * @author Sergii Karpenko\n */\npublic interface ReactiveHttpResponse {\n\n  int status();\n\n  Map<String, List<String>> headers();\n\n  Publisher<?> body();\n\n  /**\n   * used by error decoders\n   * \n   * @return error message data\n   */\n  Mono<byte[]> bodyData();\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ReadTimeoutException.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\npublic class ReadTimeoutException extends RuntimeException {\n\n  public ReadTimeoutException(Throwable cause) {\n    super(cause);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/ResponseMappers.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\nimport feign.MethodMetadata;\nimport org.apache.commons.httpclient.HttpStatus;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Mono;\n\nimport java.util.function.BiFunction;\n\n/**\n * Maps 404 error response to successful empty response\n *\n * @author Sergii Karpenko\n */\npublic class ResponseMappers {\n\n  public static BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> ignore404() {\n    return (MethodMetadata methodMetadata, ReactiveHttpResponse response) -> {\n      if (response.status() == HttpStatus.SC_NOT_FOUND) {\n        return new DelegatingReactiveHttpResponse(response) {\n          @Override\n          public int status() {\n            return HttpStatus.SC_OK;\n          }\n\n          @Override\n          public Publisher<Object> body() {\n            return Mono.empty();\n          }\n        };\n      }\n      return response;\n    };\n  }\n\n  public static ReactiveHttpClient mapResponse(\n          ReactiveHttpClient reactiveHttpClient,\n          MethodMetadata methodMetadata,\n          BiFunction<MethodMetadata, ReactiveHttpResponse, ReactiveHttpResponse> responseMapper) {\n    return request -> reactiveHttpClient.executeRequest(request)\n        .map(response -> responseMapper.apply(methodMetadata, response));\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/StatusHandlerReactiveHttpClient.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client;\n\nimport feign.MethodMetadata;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.statushandler.ReactiveStatusHandler;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport static reactivefeign.utils.FeignUtils.methodTag;\n\n/**\n * Uses statusHandlers to process status of http response\n *\n * @author Sergii Karpenko\n */\n\npublic class StatusHandlerReactiveHttpClient implements ReactiveHttpClient {\n\n  private final ReactiveHttpClient reactiveClient;\n  private final String methodTag;\n\n  private final ReactiveStatusHandler statusHandler;\n\n  public static ReactiveHttpClient handleStatus(\n          ReactiveHttpClient reactiveClient,\n          MethodMetadata methodMetadata,\n          ReactiveStatusHandler statusHandler) {\n    return new StatusHandlerReactiveHttpClient(reactiveClient, methodMetadata, statusHandler);\n  }\n\n  private StatusHandlerReactiveHttpClient(ReactiveHttpClient reactiveClient,\n                                          MethodMetadata methodMetadata,\n                                          ReactiveStatusHandler statusHandler) {\n    this.reactiveClient = reactiveClient;\n    this.methodTag = methodTag(methodMetadata);\n    this.statusHandler = statusHandler;\n  }\n\n  @Override\n  public Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request) {\n    return reactiveClient.executeRequest(request).map(response -> {\n      if (statusHandler.shouldHandle(response.status())) {\n        return new ErrorReactiveHttpResponse(response, statusHandler.decode(methodTag, response));\n      } else {\n        return response;\n      }\n    });\n  }\n\n  private class ErrorReactiveHttpResponse extends DelegatingReactiveHttpResponse {\n\n    private final Mono<? extends Throwable> error;\n\n    protected ErrorReactiveHttpResponse(ReactiveHttpResponse response, Mono<? extends Throwable> error) {\n      super(response);\n      this.error = error;\n    }\n\n    @Override\n    public Publisher<Object> body() {\n      if (getResponse().body() instanceof Mono) {\n        return error.flatMap(Mono::error);\n      } else {\n        return error.flatMapMany(Flux::error);\n      }\n    }\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/statushandler/CompositeStatusHandler.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client.statushandler;\n\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\n\nimport static java.util.Arrays.asList;\n\n/**\n * @author Sergii Karpenko\n */\npublic class CompositeStatusHandler implements ReactiveStatusHandler {\n\n  private final List<ReactiveStatusHandler> handlers;\n\n  public static CompositeStatusHandler compose(ReactiveStatusHandler... handlers) {\n    return new CompositeStatusHandler(asList(handlers));\n  }\n\n  private CompositeStatusHandler(List<ReactiveStatusHandler> handlers) {\n    this.handlers = handlers;\n  }\n\n  @Override\n  public boolean shouldHandle(int status) {\n    return handlers.stream().anyMatch(handler -> handler.shouldHandle(status));\n  }\n\n  @Override\n  public Mono<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response) {\n    return handlers.stream()\n        .filter(statusHandler -> statusHandler\n            .shouldHandle(response.status()))\n        .findFirst()\n        .map(statusHandler -> statusHandler.decode(methodKey, response))\n        .orElse(null);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/statushandler/ReactiveStatusHandler.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client.statushandler;\n\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactor.core.publisher.Mono;\n\n/**\n * @author Sergii Karpenko\n */\npublic interface ReactiveStatusHandler {\n\n  boolean shouldHandle(int status);\n\n  Mono<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response);\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/client/statushandler/ReactiveStatusHandlers.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.client.statushandler;\n\nimport feign.Response;\nimport feign.codec.ErrorDecoder;\nimport org.apache.commons.httpclient.HttpStatus;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactor.core.publisher.Mono;\n\nimport java.util.Map;\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\nimport java.util.stream.Collectors;\n\nimport static reactivefeign.utils.HttpUtils.familyOf;\n\npublic class ReactiveStatusHandlers {\n\n  public static ReactiveStatusHandler defaultFeign(ErrorDecoder errorDecoder) {\n    return new ReactiveStatusHandler() {\n\n      @Override\n      public boolean shouldHandle(int status) {\n        return familyOf(status).isError();\n      }\n\n      @Override\n      public Mono<? extends Throwable> decode(String methodTag, ReactiveHttpResponse response) {\n        return response.bodyData()\n                .defaultIfEmpty(new byte[0])\n                .map(bodyData -> errorDecoder.decode(methodTag,\n                        Response.builder().status(response.status())\n                                .reason(HttpStatus.getStatusText(response.status()))\n                                .headers(response.headers().entrySet()\n                                        .stream()\n                                        .collect(Collectors.toMap(Map.Entry::getKey,\n                                                Map.Entry::getValue)))\n                                .body(bodyData).build()));\n      }\n    };\n  }\n\n  public static ReactiveStatusHandler throwOnStatus(\n          Predicate<Integer> statusPredicate,\n          BiFunction<String, ReactiveHttpResponse, Throwable> errorFunction) {\n    return new ReactiveStatusHandler() {\n      @Override\n      public boolean shouldHandle(int status) {\n        return statusPredicate.test(status);\n      }\n\n      @Override\n      public Mono<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response) {\n        return Mono.just(errorFunction.apply(methodKey, response));\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/DefaultMethodHandler.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.methodhandler;\n\nimport java.lang.invoke.MethodHandle;\nimport java.lang.invoke.MethodHandles.Lookup;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.Method;\n\n/**\n * Handles default methods by directly invoking the default method code on the interface. The bindTo\n * method must be called on the result before invoke is called.\n */\npublic final class DefaultMethodHandler implements MethodHandler {\n  // Uses Java 7 MethodHandle based reflection. As default methods will only exist when\n  // run on a Java 8 JVM this will not affect use on legacy JVMs.\n  private final MethodHandle unboundHandle;\n\n  // handle is effectively final after bindTo has been called.\n  private MethodHandle handle;\n\n  public DefaultMethodHandler(Method defaultMethod) {\n    try {\n      Class<?> declaringClass = defaultMethod.getDeclaringClass();\n      Field field = Lookup.class.getDeclaredField(\"IMPL_LOOKUP\");\n      field.setAccessible(true);\n      Lookup lookup = (Lookup) field.get(null);\n\n      this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass);\n    } catch (NoSuchFieldException | IllegalAccessException ex) {\n      throw new IllegalStateException(ex);\n    }\n  }\n\n  /**\n   * Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it\n   * was called on the proxy object. Must be called once and only once for a given instance of\n   * DefaultMethodHandler\n   */\n  public void bindTo(Object proxy) {\n    if (handle != null) {\n      throw new IllegalStateException(\n          \"Attempted to rebind a default method handler that was already bound\");\n    }\n    handle = unboundHandle.bindTo(proxy);\n  }\n\n  /**\n   * Invoke this method. DefaultMethodHandler#bindTo must be called before the first time invoke is\n   * called.\n   */\n  @Override\n  public Object invoke(Object[] argv) throws Throwable {\n    if (handle == null) {\n      throw new IllegalStateException(\n          \"Default method handler invoked before proxy has been bound.\");\n    }\n    return handle.invokeWithArguments(argv);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/FluxMethodHandler.java",
    "content": "package reactivefeign.methodhandler;\n\nimport reactor.core.publisher.Flux;\n\npublic class FluxMethodHandler implements MethodHandler {\n\n\tprivate final MethodHandler methodHandler;\n\n\tpublic FluxMethodHandler(MethodHandler methodHandler) {\n\t\tthis.methodHandler = methodHandler;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Flux<Object> invoke(final Object[] argv) {\n\t\ttry {\n\t\t\treturn (Flux<Object>)methodHandler.invoke(argv);\n\t\t} catch (Throwable throwable) {\n\t\t\treturn Flux.error(throwable);\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/MethodHandler.java",
    "content": "package reactivefeign.methodhandler;\n\nimport feign.InvocationHandlerFactory;\n\npublic interface MethodHandler extends InvocationHandlerFactory.MethodHandler{ }\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/MethodHandlerFactory.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport reactivefeign.methodhandler.MethodHandler;\n\nimport java.lang.reflect.Method;\n\npublic interface MethodHandlerFactory {\n\n  MethodHandler create(final Target target, final MethodMetadata metadata);\n\n  MethodHandler createDefault(Method method);\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/MonoMethodHandler.java",
    "content": "package reactivefeign.methodhandler;\n\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\npublic class MonoMethodHandler implements MethodHandler {\n\n\tprivate final MethodHandler methodHandler;\n\n\tpublic MonoMethodHandler(MethodHandler methodHandler) {\n\t\tthis.methodHandler = methodHandler;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Mono<Object> invoke(final Object[] argv) {\n\t\ttry {\n\t\t\treturn (Mono<Object>)methodHandler.invoke(argv);\n\t\t} catch (Throwable throwable) {\n\t\t\treturn Mono.error(throwable);\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/PublisherClientMethodHandler.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactivefeign.publisher.PublisherHttpClient;\nimport reactivefeign.utils.Pair;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.Type;\nimport java.net.URI;\nimport java.net.URISyntaxException;\nimport java.util.*;\nimport java.util.function.Function;\nimport java.util.regex.Matcher;\nimport java.util.regex.Pattern;\nimport java.util.stream.Collectors;\nimport java.util.stream.Stream;\n\nimport static feign.Util.checkNotNull;\nimport static java.util.stream.Collectors.*;\nimport static reactivefeign.utils.FeignUtils.returnPublisherType;\nimport static reactivefeign.utils.MultiValueMapUtils.*;\n\n/**\n * Method handler for asynchronous HTTP requests via {@link PublisherHttpClient}.\n *\n * Transforms method invocation into request that executed by {@link ReactiveHttpClient}.\n *\n * @author Sergii Karpenko\n */\npublic class PublisherClientMethodHandler implements MethodHandler {\n\n  private final Target target;\n  private final MethodMetadata methodMetadata;\n  private final PublisherHttpClient publisherClient;\n  private final Function<Map<String, ?>, String> pathExpander;\n  private final Map<String, List<Function<Map<String, ?>, String>>> headerExpanders;\n  private final Map<String, Collection<String>> queriesAll;\n  private final Map<String, List<Function<Map<String, ?>, String>>> queryExpanders;\n\n  public PublisherClientMethodHandler(Target target,\n                                       MethodMetadata methodMetadata,\n                                       PublisherHttpClient publisherClient) {\n    this.target = checkNotNull(target, \"target must be not null\");\n    this.methodMetadata = checkNotNull(methodMetadata,\n        \"methodMetadata must be not null\");\n    this.publisherClient = checkNotNull(publisherClient, \"client must be not null\");\n    this.pathExpander = buildExpandFunction(methodMetadata.template().url());\n    this.headerExpanders = buildExpanders(methodMetadata.template().headers());\n\n    this.queriesAll = new HashMap<>(methodMetadata.template().queries());\n    if (methodMetadata.formParams() != null) {\n      methodMetadata.formParams()\n          .forEach(param -> add(queriesAll, param, \"{\" + param + \"}\"));\n    }\n    this.queryExpanders = buildExpanders(queriesAll);\n  }\n\n  @Override\n  @SuppressWarnings(\"unchecked\")\n  public Publisher<?> invoke(final Object[] argv) {\n\n    final ReactiveHttpRequest request = buildRequest(argv);\n\n    return publisherClient.executeRequest(request);\n  }\n\n  protected ReactiveHttpRequest buildRequest(Object[] argv) {\n\n    Map<String, ?> substitutionsMap = methodMetadata.indexToName().entrySet().stream()\n        .flatMap(e -> e.getValue().stream()\n            .map(v -> new AbstractMap.SimpleImmutableEntry<>(e.getKey(), v)))\n        .collect(Collectors.toMap(Map.Entry::getValue,\n            entry -> argv[entry.getKey()]));\n\n    try {\n      String path = pathExpander.apply(substitutionsMap);\n      Map<String, Collection<String>> queries = queries(argv, substitutionsMap);\n      Map<String, List<String>> headers = headers(argv, substitutionsMap);\n\n      URI uri = new URI(target.url() + path + queryLine(queries));\n\n      return new ReactiveHttpRequest(methodMetadata.template().method(), uri, headers, body(argv));\n\n    } catch (URISyntaxException e) {\n      throw new RuntimeException(e);\n    }\n  }\n\n  private String queryLine(Map<String, Collection<String>> queries) {\n    if (queries.isEmpty()) {\n      return \"\";\n    }\n\n    StringBuilder queryBuilder = new StringBuilder();\n    for (Map.Entry<String, Collection<String>> query : queries.entrySet()) {\n      String field = query.getKey();\n      for (String value : query.getValue()) {\n        queryBuilder.append('&');\n        queryBuilder.append(field);\n        if (value != null) {\n          queryBuilder.append('=');\n          if (!value.isEmpty()) {\n            queryBuilder.append(value);\n          }\n        }\n      }\n    }\n    queryBuilder.deleteCharAt(0);\n    return queryBuilder.insert(0, '?').toString();\n  }\n\n  protected Map<String, Collection<String>> queries(Object[] argv,\n                                                    Map<String, ?> substitutionsMap) {\n    Map<String, Collection<String>> queries = new LinkedHashMap<>();\n\n    // queries from template\n    queriesAll.keySet()\n        .forEach(queryName -> addAll(queries, queryName,\n            queryExpanders.get(queryName).stream()\n                .map(expander -> expander.apply(substitutionsMap))\n                .collect(toList())));\n\n    // queries from args\n    if (methodMetadata.queryMapIndex() != null) {\n      ((Map<String, ?>) argv[methodMetadata.queryMapIndex()])\n          .forEach((key, value) -> {\n            if (value instanceof Iterable) {\n              ((Iterable<?>) value).forEach(element -> add(queries, key, element.toString()));\n            } else {\n              add(queries, key, value.toString());\n            }\n          });\n    }\n\n    return queries;\n  }\n\n  protected Map<String, List<String>> headers(Object[] argv, Map<String, ?> substitutionsMap) {\n\n    Map<String, List<String>> headers = new LinkedHashMap<>();\n\n    // headers from template\n    methodMetadata.template().headers().keySet()\n        .forEach(headerName -> addAllOrdered(headers, headerName,\n            headerExpanders.get(headerName).stream()\n                .map(expander -> expander.apply(substitutionsMap))\n                .collect(toList())));\n\n    // headers from args\n    if (methodMetadata.headerMapIndex() != null) {\n      ((Map<String, ?>) argv[methodMetadata.headerMapIndex()])\n          .forEach((key, value) -> {\n            if (value instanceof Iterable) {\n              ((Iterable<?>) value)\n                  .forEach(element -> addOrdered(headers, key, element.toString()));\n            } else {\n              addOrdered(headers, key, value.toString());\n            }\n          });\n    }\n\n    return headers;\n  }\n\n  protected Publisher<Object> body(Object[] argv) {\n    if (methodMetadata.bodyIndex() != null) {\n      return body(argv[methodMetadata.bodyIndex()]);\n    } else {\n      return Mono.empty();\n    }\n  }\n\n  protected Publisher<Object> body(Object body) {\n    if (body instanceof Publisher) {\n      return (Publisher<Object>) body;\n    } else {\n      return Mono.just(body);\n    }\n  }\n\n  private static Map<String, List<Function<Map<String, ?>, String>>> buildExpanders(\n          Map<String, Collection<String>> templates) {\n    Stream<Pair<String, String>> headersFlattened = templates.entrySet().stream()\n        .flatMap(e -> e.getValue().stream()\n            .map(v -> new Pair<>(e.getKey(), v)));\n    return headersFlattened.collect(groupingBy(\n        entry -> entry.left,\n        mapping(entry -> buildExpandFunction(entry.right), toList())));\n  }\n\n  /**\n   *\n   * @param template\n   * @return function that able to map substitutions map to actual value for specified template\n   */\n  private static final Pattern PATTERN = Pattern.compile(\"\\\\{([^}]+)\\\\}\");\n\n  private static Function<Map<String, ?>, String> buildExpandFunction(String template) {\n    List<Function<Map<String, ?>, String>> chunks = new ArrayList<>();\n    Matcher matcher = PATTERN.matcher(template);\n    int previousMatchEnd = 0;\n    while (matcher.find()) {\n      String textChunk = template.substring(previousMatchEnd, matcher.start());\n      if (textChunk.length() > 0) {\n        chunks.add(data -> textChunk);\n      }\n\n      String substitute = matcher.group(1);\n      chunks.add(data -> {\n        Object substitution = data.get(substitute);\n        if (substitution != null) {\n          return substitution.toString();\n        } else {\n          return substitute;\n        }\n      });\n      previousMatchEnd = matcher.end();\n    }\n\n    String textChunk = template.substring(previousMatchEnd, template.length());\n    if (textChunk.length() > 0) {\n      chunks.add(data -> textChunk);\n    }\n\n    return traceData -> chunks.stream().map(chunk -> chunk.apply(traceData))\n        .collect(Collectors.joining());\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/methodhandler/ReactiveMethodHandlerFactory.java",
    "content": "package reactivefeign.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport reactivefeign.publisher.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.Method;\nimport java.lang.reflect.Type;\n\nimport static feign.Util.checkNotNull;\nimport static reactivefeign.utils.FeignUtils.returnPublisherType;\n\npublic class ReactiveMethodHandlerFactory implements MethodHandlerFactory {\n\n\tprivate final PublisherClientFactory publisherClientFactory;\n\n\tpublic ReactiveMethodHandlerFactory(final PublisherClientFactory publisherClientFactory) {\n\t\tthis.publisherClientFactory = checkNotNull(publisherClientFactory, \"client must not be null\");\n\t}\n\n\t@Override\n\tpublic MethodHandler create(Target target, MethodMetadata metadata) {\n\n\t\tMethodHandler methodHandler = new PublisherClientMethodHandler(\n\t\t\t\ttarget, metadata, publisherClientFactory.apply(metadata));\n\n\t\tType returnPublisherType = returnPublisherType(metadata);\n\t\tif(returnPublisherType == Mono.class){\n\t\t\treturn new MonoMethodHandler(methodHandler);\n\t\t} else if(returnPublisherType == Flux.class) {\n\t\t\treturn new FluxMethodHandler(methodHandler);\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\"Unknown returnPublisherType: \" + returnPublisherType);\n\t\t}\n\t}\n\n\t@Override\n\tpublic MethodHandler createDefault(Method method) {\n\t\tMethodHandler defaultMethodHandler = new DefaultMethodHandler(method);\n\n\t\tif(method.getReturnType() == Mono.class){\n\t\t\treturn new MonoMethodHandler(defaultMethodHandler);\n\t\t} else if(method.getReturnType() == Flux.class) {\n\t\t\treturn new FluxMethodHandler(defaultMethodHandler);\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\"Unknown returnPublisherType: \" + method.getReturnType());\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/FluxPublisherHttpClient.java",
    "content": "package reactivefeign.publisher;\n\n\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\n/**\n * Wraps {@link PublisherHttpClient}\n *\n * @author Sergii Karpenko\n */\npublic class FluxPublisherHttpClient implements PublisherHttpClient {\n\n\tprivate final ReactiveHttpClient reactiveHttpClient;\n\n\tpublic FluxPublisherHttpClient(ReactiveHttpClient reactiveHttpClient) {\n\t\tthis.reactiveHttpClient = reactiveHttpClient;\n\t}\n\n\t@Override\n\tpublic Flux<?> executeRequest(ReactiveHttpRequest request) {\n\t\tMono<ReactiveHttpResponse> response = reactiveHttpClient.executeRequest(request);\n\t\treturn response.flatMapMany(ReactiveHttpResponse::body);\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/FluxRetryPublisherHttpClient.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.publisher;\n\nimport feign.MethodMetadata;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.function.Function;\n\n/**\n * Wraps {@link PublisherHttpClient} with retry logic provided by retryFunction\n *\n * @author Sergii Karpenko\n */\npublic class FluxRetryPublisherHttpClient extends RetryPublisherHttpClient<FluxPublisherHttpClient> {\n\n  public FluxRetryPublisherHttpClient(\n          FluxPublisherHttpClient publisherClient, MethodMetadata methodMetadata,\n          Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {\n    super(publisherClient, methodMetadata, retryFunction);\n  }\n\n  @Override\n  public Publisher<?> executeRequest(ReactiveHttpRequest request) {\n    Flux<?> response = publisherClient.executeRequest(request);\n    return response.retryWhen(retryFunction).onErrorMap(outOfRetries());\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/MonoPublisherHttpClient.java",
    "content": "package reactivefeign.publisher;\n\n\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactor.core.publisher.Mono;\n\n/**\n * Wraps {@link PublisherHttpClient}\n *\n * @author Sergii Karpenko\n */\npublic class MonoPublisherHttpClient implements PublisherHttpClient {\n\n\tprivate final ReactiveHttpClient reactiveHttpClient;\n\n\tpublic MonoPublisherHttpClient(ReactiveHttpClient reactiveHttpClient) {\n\t\tthis.reactiveHttpClient = reactiveHttpClient;\n\t}\n\n\t@Override\n\tpublic Mono<?> executeRequest(ReactiveHttpRequest request) {\n\t\tMono<ReactiveHttpResponse> response = reactiveHttpClient.executeRequest(request);\n\t\treturn response.flatMap(resp -> Mono.from(resp.body()));\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/MonoRetryPublisherHttpClient.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.publisher;\n\nimport feign.MethodMetadata;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.function.Function;\n\n/**\n * Wraps {@link PublisherHttpClient} with retry logic provided by retryFunction\n *\n * @author Sergii Karpenko\n */\npublic class MonoRetryPublisherHttpClient extends RetryPublisherHttpClient<MonoPublisherHttpClient> {\n\n  public MonoRetryPublisherHttpClient(\n          MonoPublisherHttpClient publisherClient, MethodMetadata methodMetadata,\n          Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {\n    super(publisherClient, methodMetadata, retryFunction);\n  }\n\n  @Override\n  public Publisher<?> executeRequest(ReactiveHttpRequest request) {\n    Mono<?> response = publisherClient.executeRequest(request);\n    return response.retryWhen(retryFunction).onErrorMap(outOfRetries());\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/PublisherClientFactory.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.publisher;\n\nimport feign.MethodMetadata;\n\nimport java.util.function.Function;\n\n/**\n * @author Sergii Karpenko\n */\n\npublic interface PublisherClientFactory extends Function<MethodMetadata, PublisherHttpClient> {\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/PublisherHttpClient.java",
    "content": "package reactivefeign.publisher;\n\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpRequest;\n\nimport java.lang.reflect.Type;\n\n/**\n * @author Sergii Karpenko\n */\npublic interface PublisherHttpClient {\n\n\tPublisher<?> executeRequest(ReactiveHttpRequest request);\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/publisher/RetryPublisherHttpClient.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.publisher;\n\nimport feign.MethodMetadata;\nimport org.slf4j.Logger;\nimport org.slf4j.LoggerFactory;\nimport reactor.core.publisher.Flux;\n\nimport java.util.function.Function;\n\nimport static reactivefeign.utils.FeignUtils.methodTag;\n\n/**\n * Wraps {@link PublisherHttpClient} with retry logic provided by retryFunction\n *\n * @author Sergii Karpenko\n */\nabstract public class RetryPublisherHttpClient<P extends PublisherHttpClient> implements PublisherHttpClient {\n\n  private static final Logger logger = LoggerFactory.getLogger(RetryPublisherHttpClient.class);\n\n  private final String feignMethodTag;\n  protected final P publisherClient;\n  protected final Function<Flux<Throwable>, Flux<?>> retryFunction;\n\n  protected RetryPublisherHttpClient(P publisherClient,\n                                   MethodMetadata methodMetadata,\n                                   Function<Flux<Throwable>, Flux<Throwable>> retryFunction) {\n    this.publisherClient = publisherClient;\n    this.feignMethodTag = methodTag(methodMetadata);\n    this.retryFunction = wrapWithLog(retryFunction, feignMethodTag);\n  }\n\n  protected Function<Throwable, Throwable> outOfRetries() {\n    return throwable -> {\n      logger.debug(\"[{}]---> USED ALL RETRIES\", feignMethodTag, throwable);\n      return new OutOfRetriesException(throwable, feignMethodTag);\n    };\n  }\n\n  protected static Function<Flux<Throwable>, Flux<?>> wrapWithLog(\n          Function<Flux<Throwable>, Flux<Throwable>> retryFunction,\n          String feignMethodTag) {\n    return throwableFlux -> retryFunction.apply(throwableFlux)\n            .doOnNext(throwable -> {\n              if (logger.isDebugEnabled()) {\n                logger.debug(\"[{}]---> RETRYING on error\", feignMethodTag, throwable);\n              }\n            });\n  }\n\n  public static class OutOfRetriesException extends Exception {\n    OutOfRetriesException(Throwable cause, String feignMethodTag) {\n      super(\"All retries used for: \" + feignMethodTag, cause);\n    }\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/utils/FeignUtils.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.utils;\n\nimport feign.MethodMetadata;\nimport org.reactivestreams.Publisher;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\n\nimport static feign.Util.resolveLastTypeParameter;\nimport static java.util.Optional.ofNullable;\n\npublic class FeignUtils {\n\n  public static String methodTag(MethodMetadata methodMetadata) {\n    return methodMetadata.configKey().substring(0,\n            methodMetadata.configKey().indexOf('('));\n  }\n\n  public static Class returnPublisherType(MethodMetadata methodMetadata) {\n    final Type returnType = methodMetadata.returnType();\n    return (Class)((ParameterizedType) returnType).getRawType();\n  }\n\n  public static Type returnActualType(MethodMetadata methodMetadata) {\n    return resolveLastTypeParameter(methodMetadata.returnType(), returnPublisherType(methodMetadata));\n  }\n\n  public static Type bodyActualType(MethodMetadata methodMetadata) {\n    return getBodyActualType(methodMetadata.bodyType());\n  }\n\n  public static Type getBodyActualType(Type bodyType) {\n    return ofNullable(bodyType).map(type -> {\n      if (type instanceof ParameterizedType) {\n        Class<?> bodyClass = (Class<?>) ((ParameterizedType) type).getRawType();\n        if (Publisher.class.isAssignableFrom(bodyClass)) {\n          return resolveLastTypeParameter(bodyType, bodyClass);\n        }\n        else {\n          return type;\n        }\n      }\n      else {\n        return type;\n      }\n    }).orElse(null);\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/utils/HttpUtils.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.utils;\n\nimport static reactivefeign.utils.HttpUtils.StatusCodeFamily.*;\n\npublic class HttpUtils {\n\n  public static StatusCodeFamily familyOf(final int statusCode) {\n    switch (statusCode / 100) {\n      case 1:\n        return INFORMATIONAL;\n      case 2:\n        return SUCCESSFUL;\n      case 3:\n        return REDIRECTION;\n      case 4:\n        return CLIENT_ERROR;\n      case 5:\n        return SERVER_ERROR;\n      default:\n        return OTHER;\n    }\n  }\n\n  public enum StatusCodeFamily {\n    INFORMATIONAL(false), SUCCESSFUL(false), REDIRECTION(false), CLIENT_ERROR(true), SERVER_ERROR(\n        true), OTHER(false);\n\n    private final boolean error;\n\n    StatusCodeFamily(boolean error) {\n      this.error = error;\n    }\n\n    public boolean isError() {\n      return error;\n    }\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/utils/MultiValueMapUtils.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.utils;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Map;\n\npublic class MultiValueMapUtils {\n\n  public static <K, V> void addAllOrdered(Map<K, List<V>> multiMap, K key, List<V> values) {\n    multiMap.compute(key, (key_, values_) -> {\n      List<V> valuesMerged = values_ != null ? values_ : new ArrayList<>(values.size());\n      valuesMerged.addAll(values);\n      return valuesMerged;\n    });\n  }\n\n  public static <K, V> void addOrdered(Map<K, List<V>> multiMap, K key, V value) {\n    multiMap.compute(key, (key_, values_) -> {\n      List<V> valuesMerged = values_ != null ? values_ : new ArrayList<>(1);\n      valuesMerged.add(value);\n      return valuesMerged;\n    });\n  }\n\n  public static <K, V> void addAll(Map<K, Collection<V>> multiMap, K key, Collection<V> values) {\n    multiMap.compute(key, (key_, values_) -> {\n      Collection<V> valuesMerged = values_ != null ? values_ : new ArrayList<>(values.size());\n      valuesMerged.addAll(values);\n      return valuesMerged;\n    });\n  }\n\n  public static <K, V> void add(Map<K, Collection<V>> multiMap, K key, V value) {\n    multiMap.compute(key, (key_, values_) -> {\n      Collection<V> valuesMerged = values_ != null ? values_ : new ArrayList<>(1);\n      valuesMerged.add(value);\n      return valuesMerged;\n    });\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/main/java/reactivefeign/utils/Pair.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.utils;\n\npublic class Pair<L, R> {\n  public final L left;\n  public final R right;\n\n  public Pair(L left, R right) {\n    this.left = left;\n    this.right = right;\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/CompressionTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.common.Gzip;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactivefeign.testcase.domain.Bill;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.OrderGenerator;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;\n\n/**\n * Test the new capability of Reactive Feign client to support both Feign Request.Options\n * (regression) and the new ReactiveOptions configuration.\n *\n * @author Sergii Karpenko\n */\n\nabstract public class CompressionTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options);\n\n  @Test\n  public void testCompression() throws JsonProcessingException {\n\n    IceCreamOrder order = new OrderGenerator().generate(20);\n    Bill billExpected = Bill.makeBill(order);\n\n    wireMockRule.stubFor(post(urlEqualTo(\"/icecream/orders\"))\n        .withHeader(\"Accept-Encoding\", containing(\"gzip\"))\n        .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(order)))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withHeader(\"Content-Encoding\", \"gzip\")\n            .withBody(Gzip.gzip(TestUtils.MAPPER.writeValueAsString(billExpected)))));\n\n    IcecreamServiceApi client = builder(\n        new ReactiveOptions.Builder()\n            .setTryUseCompression(true)\n            .build())\n                .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    Mono<Bill> bill = client.makeOrder(order);\n    StepVerifier.create(bill)\n        .expectNextMatches(equalsComparingFieldByFieldRecursively(billExpected))\n        .verifyComplete();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/ConnectionTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport org.hamcrest.Matchers;\nimport org.junit.*;\nimport org.junit.rules.ExpectedException;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\nimport java.io.IOException;\nimport java.net.ConnectException;\nimport java.net.ServerSocket;\nimport java.net.Socket;\n\n/**\n * @author Sergii Karpenko\n */\nabstract public class ConnectionTimeoutTest {\n\n  @Rule\n  public ExpectedException expectedException = ExpectedException.none();\n\n  private ServerSocket serverSocket;\n  private Socket socket;\n  private int port;\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options);\n\n  @Before\n  public void before() throws IOException {\n    // server socket with single element backlog queue (1) and dynamicaly allocated\n    // port (0)\n    serverSocket = new ServerSocket(0, 1);\n    // just get the allocated port\n    port = serverSocket.getLocalPort();\n    // fill backlog queue by this request so consequent requests will be blocked\n    socket = new Socket();\n    socket.connect(serverSocket.getLocalSocketAddress());\n  }\n\n  @After\n  public void after() throws IOException {\n    // some cleanup\n    if (serverSocket != null && !serverSocket.isClosed()) {\n      serverSocket.close();\n    }\n  }\n\n  // TODO investigate why doesn't work on codecov.io but works locally\n  @Ignore\n  @Test\n  public void shouldFailOnConnectionTimeout() {\n\n    expectedException.expectCause(\n\n        Matchers.any(ConnectException.class));\n\n    IcecreamServiceApi client = builder(\n        new ReactiveOptions.Builder()\n            .setConnectTimeoutMillis(300)\n            .setReadTimeoutMillis(100)\n            .build())\n                .target(IcecreamServiceApi.class, \"http://localhost:\" + port);\n\n    client.findOrder(1).block();\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/ContractTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactivefeign.testcase.IcecreamServiceApiBroken;\nimport reactivefeign.testcase.IcecreamServiceApiBrokenByCopy;\n\nimport static org.hamcrest.Matchers.containsString;\n\n/**\n * @author Sergii Karpenko\n */\n\nabstract public class ContractTest {\n\n  @Rule\n  public ExpectedException expectedException = ExpectedException.none();\n\n  abstract protected <T> ReactiveFeign.Builder<T> builder();\n\n  @Test\n  public void shouldFailOnBrokenContract() {\n\n    expectedException.expect(IllegalArgumentException.class);\n    expectedException.expectMessage(containsString(\"Broken Contract\"));\n\n    this.<IcecreamServiceApi>builder()\n        .contract(targetType -> {\n          throw new IllegalArgumentException(\"Broken Contract\");\n        })\n        .target(IcecreamServiceApi.class, \"http://localhost:8888\");\n  }\n\n  @Test\n  public void shouldFailIfNotReactiveContract() {\n\n    expectedException.expect(IllegalArgumentException.class);\n    expectedException.expectMessage(containsString(\"IcecreamServiceApiBroken#findOrderBlocking(int)\"));\n\n    this.<IcecreamServiceApiBroken>builder()\n        .target(IcecreamServiceApiBroken.class, \"http://localhost:8888\");\n  }\n\n  @Test\n  public void shouldFailIfMethodOperatesWithByteArray() {\n\n    expectedException.expect(IllegalArgumentException.class);\n    expectedException.expectMessage(containsString(\"IcecreamServiceApiBrokenByCopy#findOrderCopy(int)\"));\n\n    this.<IcecreamServiceApiBrokenByCopy>builder()\n            .target(IcecreamServiceApiBrokenByCopy.class, \"http://localhost:8888\");\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/DefaultMethodTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport feign.RequestLine;\nimport org.assertj.core.api.Assertions;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.OrderGenerator;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;\n\n/**\n * @author Sergii Karpenko\n */\nabstract public class DefaultMethodTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  @Before\n  public void resetServers() {\n    wireMockRule.resetAll();\n  }\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();\n\n  abstract protected <API> ReactiveFeign.Builder<API> builder(Class<API> apiClass);\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options);\n\n  @Test\n  public void shouldProcessDefaultMethodOnProxy() throws JsonProcessingException {\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(orderStr)));\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findFirstOrder())\n        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderGenerated))\n        .verifyComplete();\n  }\n\n  @Test(expected = RuntimeException.class)\n  public void shouldNotWrapException() {\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    client.throwsException().onErrorReturn(\n        throwable -> throwable.equals(IcecreamServiceApi.RUNTIME_EXCEPTION),\n        orderGenerated).block();\n  }\n\n  @Test\n  public void shouldOverrideEquals() {\n\n    IcecreamServiceApi client = builder(\n        new ReactiveOptions.Builder()\n            .setConnectTimeoutMillis(300)\n            .setReadTimeoutMillis(100).build())\n                .target(IcecreamServiceApi.class,\n                    \"http://localhost:\" + wireMockRule.port());\n\n    IcecreamServiceApi clientWithSameTarget = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n    Assertions.assertThat(client).isEqualTo(clientWithSameTarget);\n\n    IcecreamServiceApi clientWithOtherPort = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + (wireMockRule.port() + 1));\n    Assertions.assertThat(client).isNotEqualTo(clientWithOtherPort);\n\n    OtherApi clientWithOtherInterface = builder(OtherApi.class)\n        .target(OtherApi.class, \"http://localhost:\" + wireMockRule.port());\n    Assertions.assertThat(client).isNotEqualTo(clientWithOtherInterface);\n  }\n\n  interface OtherApi {\n    @RequestLine(\"GET /icecream/flavors\")\n    Mono<String> method(String arg);\n  }\n\n  @Test\n  public void shouldOverrideHashcode() {\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    IcecreamServiceApi otherClientWithSameTarget = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    Assertions.assertThat(client.hashCode()).isEqualTo(otherClientWithSameTarget.hashCode());\n  }\n\n  @Test\n  public void shouldOverrideToString() {\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    Assertions.assertThat(client.toString())\n        .isEqualTo(\"HardCodedTarget(type=IcecreamServiceApi, \"\n            + \"url=http://localhost:\" + wireMockRule.port() + \")\");\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/LoggerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage reactivefeign;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.apache.logging.log4j.Level;\nimport org.apache.logging.log4j.LogManager;\nimport org.apache.logging.log4j.core.Appender;\nimport org.apache.logging.log4j.core.LogEvent;\nimport org.apache.logging.log4j.core.LoggerContext;\nimport org.apache.logging.log4j.core.config.Configuration;\nimport org.apache.logging.log4j.core.config.LoggerConfig;\nimport org.assertj.core.api.Condition;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mockito;\nimport reactivefeign.client.LoggerReactiveHttpClient;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactivefeign.testcase.domain.Bill;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.OrderGenerator;\nimport reactor.core.publisher.Mono;\n\nimport java.util.List;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\n/**\n * @author Sergii Karpenko\n */\nabstract public class LoggerTest {\n\n  public static final String LOGGER_NAME = LoggerReactiveHttpClient.class.getName();\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig()\n          .asynchronousResponseEnabled(true)\n          .dynamicPort());\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();\n\n  protected Appender appender;\n\n  @Test\n  public void shouldLog() throws Exception {\n\n    Level originalLevel = setLogLevel(Level.TRACE);\n\n    IceCreamOrder order = new OrderGenerator().generate(20);\n    Bill billExpected = Bill.makeBill(order);\n\n    wireMockRule.stubFor(post(urlEqualTo(\"/icecream/orders\"))\n        .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(order)))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(billExpected))));\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class,\n            \"http://localhost:\" + wireMockRule.port());\n\n    Mono<Bill> billMono = client.makeOrder(order);\n\n    // no logs before subscription\n    ArgumentCaptor<LogEvent> argumentCaptor = ArgumentCaptor.forClass(LogEvent.class);\n    Mockito.verify(appender, never()).append(argumentCaptor.capture());\n\n    billMono.block();\n\n    Mockito.verify(appender, times(7)).append(argumentCaptor.capture());\n\n    List<LogEvent> logEvents = argumentCaptor.getAllValues();\n    assertLogEvent(logEvents, 0, Level.DEBUG,\n        \"[IcecreamServiceApi#makeOrder]--->POST http://localhost\");\n    assertLogEvent(logEvents, 1, Level.TRACE,\n        \"[IcecreamServiceApi#makeOrder] REQUEST HEADERS\\n\" +\n            \"Accept:[application/json]\");\n    assertLogEvent(logEvents, 2, Level.TRACE,\n        \"[IcecreamServiceApi#makeOrder] REQUEST BODY\\n\" +\n            \"IceCreamOrder{ id=20, balls=\");\n    assertLogEvent(logEvents, 3, Level.TRACE,\n        \"[IcecreamServiceApi#makeOrder] RESPONSE HEADERS\",\n            \"Content-Type:application/json\");\n    assertLogEvent(logEvents, 4, Level.DEBUG,\n        \"[IcecreamServiceApi#makeOrder]<--- headers takes\");\n    assertLogEvent(logEvents, 5, Level.TRACE,\n        \"[IcecreamServiceApi#makeOrder] RESPONSE BODY\\n\" +\n            \"reactivefeign.testcase.domain.Bill\");\n    assertLogEvent(logEvents, 6, Level.DEBUG,\n        \"[IcecreamServiceApi#makeOrder]<--- body takes\");\n\n    setLogLevel(originalLevel);\n  }\n\n  private void assertLogEvent(List<LogEvent> events, int index, Level level, String message) {\n    assertThat(events).element(index)\n        .hasFieldOrPropertyWithValue(\"level\", level)\n        .extracting(\"message\")\n        .extractingResultOf(\"getFormattedMessage\")\n        .have(new Condition<>(o -> ((String) o).contains(message), \"check message\"));\n  }\n\n  private void assertLogEvent(List<LogEvent> events, int index, Level level, String message1, String message2) {\n    assertThat(events).element(index)\n            .hasFieldOrPropertyWithValue(\"level\", level)\n            .extracting(\"message\")\n            .extractingResultOf(\"getFormattedMessage\")\n            .have(new Condition<>(o -> ((String) o).contains(message1), \"check message1\"))\n            .have(new Condition<>(o -> ((String) o).contains(message2), \"check message2\"));;\n  }\n\n  @Before\n  public void before() {\n    appender = Mockito.mock(Appender.class);\n    when(appender.getName()).thenReturn(\"TestAppender\");\n    when(appender.isStarted()).thenReturn(true);\n    getLoggerConfig().addAppender(appender, Level.ALL, null);\n  }\n\n  private static Level setLogLevel(Level logLevel) {\n    LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);\n    Configuration configuration = loggerContext.getConfiguration();\n    LoggerConfig loggerConfig = configuration.getLoggerConfig(LOGGER_NAME);\n    Level previousLevel = loggerConfig.getLevel();\n    loggerConfig.setLevel(logLevel);\n    loggerContext.updateLoggers();\n    return previousLevel;\n  }\n\n  private static LoggerConfig getLoggerConfig() {\n    LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);\n    Configuration configuration = loggerContext.getConfiguration();\n    configuration.addLogger(LOGGER_NAME, new LoggerConfig());\n    return configuration.getLoggerConfig(LOGGER_NAME);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/NotFoundTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.apache.http.HttpStatus;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactor.test.StepVerifier;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\n\n/**\n * @author Sergii Karpenko\n */\npublic abstract class NotFoundTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();\n\n  @Test\n  public void shouldReturnEmptyMono() {\n\n    String orderUrl = \"/icecream/orders/2\";\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_NOT_FOUND)));\n\n    IcecreamServiceApi client = builder()\n        .decode404()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findOrder(2))\n        .expectNextCount(0)\n        .verifyComplete();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/ReactivityTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.awaitility.Duration;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.OrderGenerator;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static org.awaitility.Awaitility.waitAtMost;\n\n/**\n * @author Sergii Karpenko\n */\nabstract public class ReactivityTest {\n\n  public static final int DELAY_IN_MILLIS = 500;\n  public static final int CALLS_NUMBER = 500;\n  public static final int REACTIVE_GAIN_RATIO = 10;\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n          wireMockConfig()\n                  .asynchronousResponseEnabled(true)\n                  .dynamicPort());\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();\n\n  @Test\n  public void shouldRunReactively() throws JsonProcessingException {\n\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n            .withHeader(\"Accept\", equalTo(\"application/json\"))\n            .willReturn(aResponse().withStatus(200)\n                    .withHeader(\"Content-Type\", \"application/json\")\n                    .withBody(orderStr)\n                    .withFixedDelay(DELAY_IN_MILLIS)));\n\n    IcecreamServiceApi client = builder()\n            .target(IcecreamServiceApi.class,\n                    \"http://localhost:\" + wireMockRule.port());\n\n    AtomicInteger counter = new AtomicInteger();\n\n    new Thread(() -> {\n      for (int i = 0; i < CALLS_NUMBER; i++) {\n        client.findFirstOrder()\n                .doOnNext(order -> counter.incrementAndGet())\n                .subscribe();\n      }\n    }).start();\n\n    waitAtMost(new Duration(timeToCompleteReactively(), TimeUnit.MILLISECONDS))\n            .until(() -> counter.get() == CALLS_NUMBER);\n  }\n\n  public static int timeToCompleteReactively() {\n    return CALLS_NUMBER * DELAY_IN_MILLIS / REACTIVE_GAIN_RATIO;\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/ReadTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.client.ReadTimeoutException;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactor.test.StepVerifier;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\n\n/**\n * @author Sergii Karpenko\n */\nabstract public class ReadTimeoutTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options);\n\n  @Test\n  public void shouldFailOnReadTimeout() {\n\n    String orderUrl = \"/icecream/orders/1\";\n\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withFixedDelay(200)));\n\n    IcecreamServiceApi client = builder(\n        new ReactiveOptions.Builder()\n            .setConnectTimeoutMillis(300)\n            .setReadTimeoutMillis(100)\n            .build())\n                .target(IcecreamServiceApi.class,\n                    \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findOrder(1))\n        .expectError(ReadTimeoutException.class)\n        .verify();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/RequestInterceptorTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport feign.FeignException;\nimport org.apache.http.HttpStatus;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.OrderGenerator;\nimport reactivefeign.utils.Pair;\nimport reactor.test.StepVerifier;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static java.util.Collections.singletonList;\nimport static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;\n\n/**\n * @author Sergii Karpenko\n */\nabstract public class RequestInterceptorTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();\n\n  @Test\n  public void shouldInterceptRequestAndSetAuthHeader() throws JsonProcessingException {\n\n    String orderUrl = \"/icecream/orders/1\";\n\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);\n\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_UNAUTHORIZED)))\n        .setPriority(100);\n\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .withHeader(\"Authorization\", equalTo(\"Bearer mytoken123\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(orderStr)))\n        .setPriority(1);\n\n    IcecreamServiceApi clientWithoutAuth = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(clientWithoutAuth.findFirstOrder())\n        .expectError(notAuthorizedException())\n        .verify();\n\n    IcecreamServiceApi clientWithAuth = builder()\n        .addHeaders(singletonList(new Pair<>(\"Authorization\", \"Bearer mytoken123\")))\n        .target(IcecreamServiceApi.class,\n            \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(clientWithAuth.findFirstOrder())\n        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderGenerated))\n        .expectComplete();\n  }\n\n  protected Class notAuthorizedException() {\n    return FeignException.class;\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/RetryingTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport feign.RetryableException;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.publisher.RetryPublisherHttpClient;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.Mixin;\nimport reactivefeign.testcase.domain.OrderGenerator;\nimport reactor.test.StepVerifier;\n\nimport java.util.Arrays;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static com.github.tomakehurst.wiremock.stubbing.Scenario.STARTED;\nimport static org.apache.http.HttpHeaders.RETRY_AFTER;\nimport static org.apache.http.HttpStatus.SC_OK;\nimport static org.apache.http.HttpStatus.SC_SERVICE_UNAVAILABLE;\nimport static org.hamcrest.Matchers.hasProperty;\nimport static org.hamcrest.Matchers.isA;\nimport static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;\n\n/**\n * @author Sergii Karpenko\n */\npublic abstract class RetryingTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();\n\n  @Before\n  public void resetServers() {\n    wireMockRule.resetAll();\n  }\n\n  @Test\n  public void shouldSuccessOnRetriesMono() throws JsonProcessingException {\n\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);\n\n    mockResponseAfterSeveralAttempts(wireMockRule, 2, \"testRetrying_success\",\n        \"/icecream/orders/1\",\n        aResponse().withStatus(503).withHeader(RETRY_AFTER, \"1\"),\n        aResponse().withStatus(200).withHeader(\"Content-Type\", \"application/json\")\n            .withBody(orderStr));\n\n    IcecreamServiceApi client = builder()\n        .retryWhen(ReactiveRetryers.retryWithBackoff(3, 0))\n        .target(IcecreamServiceApi.class,\n            \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findOrder(1))\n        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderGenerated))\n        .verifyComplete();\n  }\n\n  @Test\n  public void shouldSuccessOnRetriesFlux() throws JsonProcessingException {\n\n    String mixinsStr = TestUtils.MAPPER.writeValueAsString(Mixin.values());\n\n    mockResponseAfterSeveralAttempts(wireMockRule, 2, \"testRetrying_success\",\n        \"/icecream/mixins\",\n        aResponse().withStatus(SC_SERVICE_UNAVAILABLE).withHeader(RETRY_AFTER,\n            \"1\"),\n        aResponse().withStatus(SC_OK)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(mixinsStr));\n\n    IcecreamServiceApi client = builder()\n        .retryWhen(ReactiveRetryers.retryWithBackoff(3, 0))\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.getAvailableMixins())\n        .expectNextSequence(Arrays.asList(Mixin.values()))\n        .verifyComplete();\n  }\n\n  @Test\n  public void shouldSuccessOnRetriesWoRetryAfter() throws JsonProcessingException {\n\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);\n\n    mockResponseAfterSeveralAttempts(wireMockRule, 2, \"testRetrying_success\",\n        \"/icecream/orders/1\", aResponse().withStatus(SC_SERVICE_UNAVAILABLE),\n        aResponse().withStatus(SC_OK)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(orderStr));\n\n    IcecreamServiceApi client = builder()\n        .retryWhen(ReactiveRetryers.retryWithBackoff(3, 0))\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findOrder(1))\n        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderGenerated))\n        .verifyComplete();\n  }\n\n  private static void mockResponseAfterSeveralAttempts(WireMockClassRule rule,\n                                                       int failedAttemptsNo,\n                                                       String scenario,\n                                                       String url,\n                                                       ResponseDefinitionBuilder failResponse,\n                                                       ResponseDefinitionBuilder response) {\n    String state = STARTED;\n    for (int attempt = 0; attempt < failedAttemptsNo; attempt++) {\n      String nextState = \"attempt\" + attempt;\n      rule.stubFor(\n          get(urlEqualTo(url))\n              .inScenario(scenario).whenScenarioStateIs(state)\n              .willReturn(failResponse).willSetStateTo(nextState));\n\n      state = nextState;\n    }\n\n    rule.stubFor(get(urlEqualTo(url))\n        .inScenario(scenario)\n        .whenScenarioStateIs(state).willReturn(response));\n  }\n\n  @Test\n  public void shouldFailAsNoMoreRetries() {\n\n    String orderUrl = \"/icecream/orders/1\";\n\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(503).withHeader(RETRY_AFTER, \"1\")));\n\n    IcecreamServiceApi client = builder()\n        .retryWhen(ReactiveRetryers.retry(3))\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findOrder(1))\n        .expectErrorMatches(throwable -> throwable instanceof RetryPublisherHttpClient.OutOfRetriesException\n            && hasProperty(\"cause\", isA(RetryableException.class)).matches(throwable))\n        .verify();\n  }\n\n  @Test\n  public void shouldFailAsNoMoreRetriesWithBackoff() {\n\n    String orderUrl = \"/icecream/orders/1\";\n\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(503).withHeader(RETRY_AFTER, \"1\")));\n\n    IcecreamServiceApi client = builder()\n        .retryWhen(ReactiveRetryers.retryWithBackoff(7, 5))\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findOrder(1))\n        .expectErrorMatches(throwable -> throwable instanceof RetryPublisherHttpClient.OutOfRetriesException\n                && hasProperty(\"cause\", isA(RetryableException.class)).matches(throwable))\n        .verify();\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/SmokeTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactivefeign.testcase.domain.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.test.StepVerifier;\n\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static java.util.Arrays.asList;\nimport static reactivefeign.TestUtils.equalsComparingFieldByFieldRecursively;\n\n/**\n * @author Sergii Karpenko\n */\n\nabstract public class SmokeTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  @Before\n  public void resetServers() {\n    wireMockRule.resetAll();\n  }\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();\n\n  private IcecreamServiceApi client;\n\n  private OrderGenerator generator = new OrderGenerator();\n  private Map<Integer, IceCreamOrder> orders = generator.generateRange(10).stream()\n      .collect(Collectors.toMap(IceCreamOrder::getId, o -> o));\n\n  @Rule\n  public ExpectedException expectedException = ExpectedException.none();\n\n  @Before\n  public void setUp() {\n    String targetUrl = \"http://localhost:\" + wireMockRule.port();\n    client = builder()\n        .decode404()\n        .target(IcecreamServiceApi.class, targetUrl);\n  }\n\n  @Test\n  public void testSimpleGet_success() throws JsonProcessingException {\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/flavors\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(Flavor.values()))));\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/mixins\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(Mixin.values()))));\n\n    Flux<Flavor> flavors = client.getAvailableFlavors();\n    Flux<Mixin> mixins = client.getAvailableMixins();\n\n    StepVerifier.create(flavors)\n        .expectNextSequence(asList(Flavor.values()))\n        .verifyComplete();\n    StepVerifier.create(mixins)\n        .expectNextSequence(asList(Mixin.values()))\n        .verifyComplete();\n\n  }\n\n  @Test\n  public void testFindOrder_success() throws JsonProcessingException {\n\n    IceCreamOrder orderExpected = orders.get(1);\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(orderExpected))));\n\n    Mono<IceCreamOrder> order = client.findOrder(1);\n\n    StepVerifier.create(order)\n        .expectNextMatches(equalsComparingFieldByFieldRecursively(orderExpected))\n        .verifyComplete();\n  }\n\n  @Test\n  public void testFindOrder_empty() {\n\n    Mono<IceCreamOrder> orderEmpty = client.findOrder(123);\n\n    StepVerifier.create(orderEmpty)\n        .expectNextCount(0)\n        .verifyComplete();\n  }\n\n  @Test\n  public void testMakeOrder_success() throws JsonProcessingException {\n\n    IceCreamOrder order = new OrderGenerator().generate(20);\n    Bill billExpected = Bill.makeBill(order);\n\n    wireMockRule.stubFor(post(urlEqualTo(\"/icecream/orders\"))\n        .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(order)))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(billExpected))));\n\n    Mono<Bill> bill = client.makeOrder(order);\n    StepVerifier.create(bill)\n        .expectNextMatches(equalsComparingFieldByFieldRecursively(billExpected))\n        .verifyComplete();\n  }\n\n  @Test\n  public void testPayBill_success() throws JsonProcessingException {\n\n    Bill bill = Bill.makeBill(new OrderGenerator().generate(30));\n\n    wireMockRule.stubFor(post(urlEqualTo(\"/icecream/bills/pay\"))\n            .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(bill)))\n            .willReturn(aResponse().withStatus(200)\n                    .withHeader(\"Content-Type\", \"application/json\")));\n\n    Mono<Void> result = client.payBill(bill);\n    StepVerifier.create(result)\n        .expectNextCount(0)\n        .verifyComplete();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/StatusHandlerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport feign.RetryableException;\nimport org.apache.http.HttpStatus;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.testcase.IcecreamServiceApi;\nimport reactor.test.StepVerifier;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static reactivefeign.client.statushandler.CompositeStatusHandler.compose;\nimport static reactivefeign.client.statushandler.ReactiveStatusHandlers.throwOnStatus;\n\n/**\n * @author Sergii Karpenko\n */\npublic abstract class StatusHandlerTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  abstract protected ReactiveFeign.Builder<IcecreamServiceApi> builder();\n\n  @Before\n  public void resetServers() {\n    wireMockRule.resetAll();\n  }\n\n  @Test\n  public void shouldThrowRetryException() {\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_SERVICE_UNAVAILABLE)));\n    IcecreamServiceApi client = builder()\n        .statusHandler(throwOnStatus(\n            status -> status == HttpStatus.SC_SERVICE_UNAVAILABLE,\n            (methodTag, response) -> new RetryableException(\"Should retry on next node\", null)))\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findFirstOrder())\n        .expectError(RetryableException.class);\n  }\n\n  @Test\n  public void shouldThrowOnStatusCode() {\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_SERVICE_UNAVAILABLE)));\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/2\"))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_UNAUTHORIZED)));\n\n\n    IcecreamServiceApi client = builder()\n        .statusHandler(compose(\n            throwOnStatus(\n                status -> status == HttpStatus.SC_SERVICE_UNAVAILABLE,\n                (methodTag, response) -> new RetryableException(\"Should retry on next node\", null)),\n            throwOnStatus(\n                status -> status == HttpStatus.SC_UNAUTHORIZED,\n                (methodTag, response) -> new RuntimeException(\"Should login\", null))))\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    StepVerifier.create(client.findFirstOrder())\n        .expectError(RetryableException.class)\n        .verify();\n\n    StepVerifier.create(client.findOrder(2))\n        .expectError(RuntimeException.class)\n        .verify();\n\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/TestUtils.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\n\nimport java.util.function.Predicate;\n\n/**\n * Helper methods for tests.\n */\nclass TestUtils {\n  static final ObjectMapper MAPPER;\n\n  static {\n    MAPPER = new ObjectMapper();\n    MAPPER.registerModule(new JavaTimeModule());\n  }\n\n  public static <T> Predicate<T> equalsComparingFieldByFieldRecursively(T rhs) {\n    return lhs -> {\n      try {\n        return MAPPER.writeValueAsString(lhs).equals(MAPPER.writeValueAsString(rhs));\n      } catch (JsonProcessingException e) {\n        throw new RuntimeException(e);\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesApi.java",
    "content": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reactivefeign.allfeatures;\n\nimport feign.*;\nimport org.reactivestreams.Publisher;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.nio.ByteBuffer;\nimport java.util.Map;\n\nimport static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;\nimport static org.springframework.http.MediaType.APPLICATION_STREAM_JSON_VALUE;\nimport static org.springframework.http.MediaType.TEXT_EVENT_STREAM_VALUE;\n\n@Headers({ \"Accept: application/json\" })\npublic interface AllFeaturesApi {\n\n\t@RequestLine(\"GET /mirrorParameters/{parameterInPathPlaceholder}?paramInUrl={paramInQueryPlaceholder}\")\n\tMono<Map<String, String>> mirrorParameters(\n            @Param(\"parameterInPathPlaceholder\") long paramInPath,\n            @Param(\"paramInQueryPlaceholder\") long paramInQuery,\n            @QueryMap Map<String, String> paramMap);\n\n\t@RequestLine(\"GET /mirrorParametersNew?paramInUrl={paramInUrlPlaceholder}\")\n\tMono<Map<String, String>> mirrorParametersNew(\n            @Param(\"paramInUrlPlaceholder\") long paramInUrl,\n            @Param(\"dynamicParam\") long dynamicParam,\n            @QueryMap Map<String, String> paramMap);\n\n\t@RequestLine(\"GET /mirrorHeaders\")\n\t@Headers({ \"Method-Header: {headerValue}\" })\n\tMono<Map<String, String>> mirrorHeaders(@Param(\"headerValue\") long param,\n                                            @HeaderMap Map<String, String> paramMap);\n\n\t@RequestLine(\"POST \" + \"/mirrorBody\")\n\tMono<String> mirrorBody(String body);\n\n\t@RequestLine(\"POST \" + \"/mirrorBodyMap\")\n\t@Headers({ \"Content-Type: application/json\" })\n\tMono<Map<String, String>> mirrorBodyMap(Map<String, String> body);\n\n\t@RequestLine(\"POST \" + \"/mirrorBodyReactive\")\n\t@Headers({ \"Content-Type: application/json\" })\n\tMono<String> mirrorBodyReactive(Publisher<String> body);\n\n\t@RequestLine(\"POST \" + \"/mirrorBodyMapReactive\")\n\t@Headers({ \"Content-Type: application/json\" })\n\tMono<Map<String, String>> mirrorBodyMapReactive(Publisher<Map<String, String>> body);\n\n\t@RequestLine(\"POST \" + \"/mirrorBodyStream\")\n\t@Headers({ \"Content-Type: \"+APPLICATION_STREAM_JSON_VALUE,\n\t\t\t   \"Accept: \"+APPLICATION_STREAM_JSON_VALUE})\n\tFlux<TestObject> mirrorBodyStream(Publisher<TestObject> bodyStream);\n\n\t@RequestLine(\"POST \" + \"/mirrorIntegerBodyStream\")\n\t@Headers({ \"Content-Type: \"+APPLICATION_STREAM_JSON_VALUE,\n\t\t\t\"Accept: \"+APPLICATION_STREAM_JSON_VALUE})\n\tFlux<Integer> mirrorIntegerBodyStream(Flux<Integer> body);\n\n\t@RequestLine(\"POST \" + \"/mirrorStringBodyStream\")\n\t@Headers({ \"Content-Type: \"+TEXT_EVENT_STREAM_VALUE,\n\t\t\t\"Accept: \"+TEXT_EVENT_STREAM_VALUE})\n\tFlux<String> mirrorStringBodyStream(Flux<String> body);\n\n\t@RequestLine(\"GET /empty\")\n\t@Headers({ \"Method-Header: {headerValue}\" })\n\tMono<TestObject> empty();\n\n\t@RequestLine(\"POST \" + \"/mirrorBodyWithDelay\")\n\tMono<String> mirrorBodyWithDelay(String body);\n\n\t@RequestLine(\"POST \" + \"/mirrorStreamingBinaryBodyReactive\")\n\t@Headers({ \"Content-Type: \"+APPLICATION_OCTET_STREAM_VALUE })\n\tFlux<ByteBuffer> mirrorStreamingBinaryBodyReactive(Publisher<ByteBuffer> body);\n\n\tdefault Mono<String> mirrorDefaultBody() {\n\t\treturn mirrorBody(\"default\");\n\t}\n\n\tclass TestObject {\n\n\t\tpublic String payload;\n\n\t\tpublic TestObject() {\n\t\t}\n\n\t\tpublic TestObject(String payload) {\n\t\t\tthis.payload = payload;\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesController.java",
    "content": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reactivefeign.allfeatures;\n\nimport org.reactivestreams.Publisher;\nimport org.springframework.http.MediaType;\nimport org.springframework.web.bind.annotation.*;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.nio.ByteBuffer;\nimport java.time.Duration;\nimport java.util.Map;\n\nimport static org.springframework.http.MediaType.APPLICATION_STREAM_JSON_VALUE;\nimport static reactor.core.publisher.Mono.just;\n\n@RestController\npublic class AllFeaturesController implements AllFeaturesApi {\n\n\t@GetMapping(path = \"/mirrorParameters/{paramInPath}\")\n\t@Override\n\tpublic Mono<Map<String, String>> mirrorParameters(\n\t\t\t@PathVariable(\"paramInPath\") long paramInPath,\n\t\t\t@RequestParam(\"paramInUrl\") long paramInUrl,\n\t\t\t@RequestParam Map<String, String> paramMap) {\n\t\tparamMap.put(\"paramInPath\", Long.toString(paramInPath));\n\t\tparamMap.put(\"paramInUrl\", Long.toString(paramInUrl));\n\t\treturn just(paramMap);\n\t}\n\n\t@GetMapping(path = \"/mirrorParametersNew\")\n\t@Override\n\tpublic Mono<Map<String, String>> mirrorParametersNew(\n\t\t\t@RequestParam(\"paramInUrl\") long paramInUrl,\n\t\t\t@RequestParam(\"dynamicParam\") long dynamicParam,\n\t\t\t@RequestParam Map<String, String> paramMap) {\n\t\tparamMap.put(\"paramInUrl\", Long.toString(paramInUrl));\n\t\tparamMap.put(\"dynamicParam\", Long.toString(dynamicParam));\n\t\treturn just(paramMap);\n\t}\n\n\t@GetMapping(path = \"/mirrorHeaders\")\n\t@Override\n\tpublic Mono<Map<String, String>> mirrorHeaders(\n\t\t\t@RequestHeader(\"Method-Header\") long param,\n\t\t\t@RequestHeader Map<String, String> headersMap) {\n\t\treturn just(headersMap);\n\t}\n\n\t@PostMapping(path = \"/mirrorBody\")\n\t@Override\n\tpublic Mono<String> mirrorBody(@RequestBody String body) {\n\t\treturn just(body);\n\t}\n\n\t@PostMapping(path = \"/mirrorBodyMap\")\n\t@Override\n\tpublic Mono<Map<String, String>> mirrorBodyMap(\n\t\t\t@RequestBody Map<String, String> body) {\n\t\treturn just(body);\n\t}\n\n\t@PostMapping(path = \"/mirrorBodyReactive\")\n\t@Override\n\tpublic Mono<String> mirrorBodyReactive(@RequestBody Publisher<String> body) {\n\t\treturn Mono.from(body);\n\t}\n\n\t@PostMapping(path = \"/mirrorBodyMapReactive\")\n\t@Override\n\tpublic Mono<Map<String, String>> mirrorBodyMapReactive(\n\t\t\t@RequestBody Publisher<Map<String, String>> body) {\n\t\treturn Mono.from(body);\n\t}\n\n\t@PostMapping(path = \"/mirrorBodyStream\")\n\t@Override\n\tpublic Flux<TestObject> mirrorBodyStream(\n\t\t\t@RequestBody Publisher<TestObject> bodyStream) {\n\t\treturn Flux.from(bodyStream);\n\t}\n\n\t@PostMapping(path = \"/mirrorIntegerBodyStream\")\n\t@Override\n\tpublic Flux<Integer> mirrorIntegerBodyStream(\n\t\t\t@RequestBody Flux<Integer> body){\n\t\treturn body;\n\t}\n\n\t@PostMapping(path = \"/mirrorStringBodyStream\")\n\t@Override\n\tpublic Flux<String> mirrorStringBodyStream(\n\t\t\t@RequestBody  Flux<String> body){\n\t\treturn body;\n\t}\n\n\t@PostMapping(path = \"/mirrorBodyWithDelay\")\n\t@Override\n\tpublic Mono<String> mirrorBodyWithDelay(@RequestBody String body) {\n\t\treturn just(body).delayElement(Duration.ofMillis(500));\n\t}\n\n\t@Override\n\tpublic Mono<TestObject> empty() {\n\t\treturn Mono.empty();\n\t}\n\n\t@PostMapping(path = \"/mirrorStreamingBinaryBodyReactive\")\n\t@Override\n\tpublic Flux<ByteBuffer> mirrorStreamingBinaryBodyReactive(@RequestBody Publisher<ByteBuffer> body) {\n\t\treturn Flux.from(body);\n\t}\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/allfeatures/AllFeaturesTest.java",
    "content": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reactivefeign.allfeatures;\n\nimport org.awaitility.Duration;\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;\nimport org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;\nimport org.springframework.boot.web.reactive.server.ReactiveWebServerFactory;\nimport org.springframework.boot.web.server.LocalServerPort;\nimport org.springframework.context.annotation.Bean;\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport reactivefeign.ReactiveFeign;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\nimport reactor.core.scheduler.Schedulers;\nimport reactor.test.StepVerifier;\n\nimport java.nio.ByteBuffer;\nimport java.util.ArrayList;\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.Optional;\nimport java.util.concurrent.CompletableFuture;\nimport java.util.concurrent.ConcurrentLinkedQueue;\nimport java.util.concurrent.CountDownLatch;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\nimport java.util.stream.Collectors;\n\nimport static java.nio.ByteBuffer.wrap;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.awaitility.Awaitility.waitAtMost;\nimport static reactivefeign.ReactivityTest.CALLS_NUMBER;\nimport static reactivefeign.ReactivityTest.timeToCompleteReactively;\nimport static reactor.core.publisher.Flux.empty;\nimport static reactor.core.publisher.Mono.fromFuture;\nimport static reactor.core.publisher.Mono.just;\n\n/**\n * @author Sergii Karpenko\n *\n * Tests ReactiveFeign in conjunction with WebFlux rest controller.\n */\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(\n\t\tproperties = {\"spring.main.web-application-type=reactive\"},\n\t\tclasses = {AllFeaturesController.class },\n\t\twebEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@EnableAutoConfiguration(exclude = {ReactiveSecurityAutoConfiguration.class, ReactiveUserDetailsServiceAutoConfiguration.class})\nabstract public class AllFeaturesTest {\n\n\tprivate AllFeaturesApi client;\n\n\t@LocalServerPort\n\tprivate int port;\n\n\t@Rule\n\tpublic ExpectedException expectedException = ExpectedException.none();\n\n\tabstract protected ReactiveFeign.Builder<AllFeaturesApi> builder();\n\n\t@Before\n\tpublic void setUp() {\n\t\tclient = builder()\n\t\t\t\t.decode404()\n\t\t\t\t.target(AllFeaturesApi.class, \"http://localhost:\" + port);\n\t}\n\n\t@Test\n\tpublic void shouldReturnAllPassedParameters() {\n\t\tMap<String, String> paramMap = new HashMap<String, String>() {\n\t\t\t{\n\t\t\t\tput(\"paramKey\", \"paramValue\");\n\t\t\t}\n\t\t};\n\t\tMap<String, String> returned = client.mirrorParameters(555,777, paramMap).block();\n\n\t\tassertThat(returned).containsEntry(\"paramInPath\", \"555\");\n\t\tassertThat(returned).containsEntry(\"paramInUrl\", \"777\");\n\t\tassertThat(returned).containsAllEntriesOf(paramMap);\n\t}\n\n\t@Test\n\tpublic void shouldReturnAllPassedParametersNew() {\n\t\tMap<String, String> paramMap = new HashMap<String, String>() {\n\t\t\t{\n\t\t\t\tput(\"paramKey\", \"paramValue\");\n\t\t\t}\n\t\t};\n\t\tMap<String, String> returned = client.mirrorParametersNew(777, 888, paramMap)\n\t\t\t\t.block();\n\n\t\tassertThat(returned).containsEntry(\"paramInUrl\", \"777\");\n\t\tassertThat(returned).containsEntry(\"dynamicParam\", \"888\");\n\t\tassertThat(returned).containsAllEntriesOf(paramMap);\n\t}\n\n\t@Test\n\tpublic void shouldReturnAllPassedHeaders() {\n\t\tMap<String, String> headersMap = new HashMap<String, String>() {\n\t\t\t{\n\t\t\t\tput(\"headerKey1\", \"headerValue1\");\n\t\t\t\tput(\"headerKey2\", \"headerValue2\");\n\t\t\t}\n\t\t};\n\t\tMap<String, String> returned = client.mirrorHeaders(777, headersMap).block();\n\n\t\tassertThat(returned).containsEntry(\"Method-Header\", \"777\");\n\t\tassertThat(returned).containsAllEntriesOf(headersMap);\n\t\tassertThat(returned).containsKey(\"Accept\");\n\t}\n\n\t@Test\n\tpublic void shouldReturnBody() {\n\t\tString returned = client.mirrorBody(\"Test Body\").block();\n\n\t\tassertThat(returned).isEqualTo(\"Test Body\");\n\t}\n\n\t@Test\n\tpublic void shouldReturnBodyMap() {\n\t\tMap<String, String> bodyMap = new HashMap<String, String>() {\n\t\t\t{\n\t\t\t\tput(\"key1\", \"value1\");\n\t\t\t\tput(\"key2\", \"value2\");\n\t\t\t}\n\t\t};\n\n\t\tMap<String, String> returned = client.mirrorBodyMap(bodyMap).block();\n\t\tassertThat(returned).containsAllEntriesOf(bodyMap);\n\t}\n\n\t@Test\n\tpublic void shouldReturnBodyReactive() {\n\t\tString returned = client.mirrorBodyReactive(just(\"Test Body\")).block();\n\t\tassertThat(returned).isEqualTo(\"Test Body\");\n\t}\n\n\t@Test\n\tpublic void shouldReturnBodyMapReactive() {\n\t\tMap<String, String> bodyMap = new HashMap<String, String>() {\n\t\t\t{\n\t\t\t\tput(\"key1\", \"value1\");\n\t\t\t\tput(\"key2\", \"value2\");\n\t\t\t}\n\t\t};\n\n\t\tMono<Map<String, String>> publisher = client.mirrorBodyMapReactive(just(bodyMap));\n\t\tStepVerifier.create(publisher)\n\t\t\t\t.consumeNextWith(map -> assertThat(map).containsAllEntriesOf(bodyMap))\n\t\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void shouldReturnFirstResultBeforeSecondSent() throws InterruptedException {\n\n\t\tCountDownLatch countDownLatch = new CountDownLatch(2);\n\n\t\tAtomicInteger sentCount = new AtomicInteger();\n\t\tAtomicInteger receivedCount = new AtomicInteger();\n\n\t\tCompletableFuture<AllFeaturesApi.TestObject> firstReceived = new CompletableFuture<>();\n\n\t\tFlux<AllFeaturesApi.TestObject> returned = client\n\t\t\t\t.mirrorBodyStream(Flux.just(new AllFeaturesApi.TestObject(\"testMessage1\"),\n\t\t\t\t\t\tnew AllFeaturesApi.TestObject(\"testMessage2\"))\n\t\t\t\t\t\t.delayUntil(testObject -> sentCount.get() == 1 ? fromFuture(firstReceived)\n\t\t\t\t\t\t\t\t: empty())\n\t\t\t\t\t\t.doOnNext(sent -> sentCount.incrementAndGet())\n\t\t\t\t);\n\n\t\treturned.doOnNext(received -> {\n\t\t\treceivedCount.incrementAndGet();\n\t\t\tfirstReceived.complete(received);\n\t\t\tcountDownLatch.countDown();\n\t\t}).subscribe();\n\n\t\tcountDownLatch.await();\n\t}\n\n\t@Test\n\tpublic void shouldReturnEmpty() {\n\t\tOptional<AllFeaturesApi.TestObject> returned = client.empty().blockOptional();\n\t\tassertThat(!returned.isPresent());\n\t}\n\n\t@Test\n\tpublic void shouldReturnDefaultBody() {\n\t\tString returned = client.mirrorDefaultBody().block();\n\t\tassertThat(returned).isEqualTo(\"default\");\n\t}\n\n\n\t@Test\n\tpublic void shouldRunReactively() {\n\n\t\tAtomicInteger counter = new AtomicInteger();\n\n\t\tfor (int i = 0; i < CALLS_NUMBER; i++) {\n\t\t\tclient.mirrorBodyWithDelay(\"testBody\")\n\t\t\t\t\t.doOnNext(order -> counter.incrementAndGet())\n\t\t\t\t\t.subscribe();\n\t\t}\n\n\t\twaitAtMost(new Duration(timeToCompleteReactively(), TimeUnit.MILLISECONDS))\n\t\t\t\t.until(() -> counter.get() == CALLS_NUMBER);\n\t}\n\n\t@Test\n\tpublic void shouldMirrorIntegerStreamBody() {\n\t\tFlux<Integer> result = client.mirrorIntegerBodyStream(\n\t\t\t\tFlux.fromArray(new Integer[]{1, 3, 5, 7}));\n\n\t\tStepVerifier.create(result)\n\t\t\t\t.expectNext(1)\n\t\t\t\t.expectNext(3)\n\t\t\t\t.expectNext(5)\n\t\t\t\t.expectNext(7)\n\t\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void shouldMirrorStringStreamBody() {\n\t\tFlux<String> result = client.mirrorStringBodyStream(\n\t\t\t\tFlux.fromArray(new String[]{\"a\", \"b\", \"c\"}));\n\n\t\tStepVerifier.create(result)\n\t\t\t\t.expectNext(\"a\")\n\t\t\t\t.expectNext(\"b\")\n\t\t\t\t.expectNext(\"c\")\n\t\t\t\t.verifyComplete();\n\t}\n\n\t@Test\n\tpublic void shouldMirrorStreamingBinaryBodyReactive() throws InterruptedException {\n\n\t\tCountDownLatch countDownLatch = new CountDownLatch(2);\n\n\t\tAtomicInteger sentCount = new AtomicInteger();\n\t\tConcurrentLinkedQueue<byte[]> receivedAll = new ConcurrentLinkedQueue<>();\n\n\t\tCompletableFuture<ByteBuffer> firstReceived = new CompletableFuture<>();\n\n\t\tFlux<ByteBuffer> returned = client\n\t\t\t\t.mirrorStreamingBinaryBodyReactive(Flux.just(\n\t\t\t\t\t\tfromByteArray(new byte[]{1,2,3}),\n\t\t\t\t\t\tfromByteArray(new byte[]{4,5,6})))\n\t\t\t\t.delayUntil(testObject -> sentCount.get() == 1 ? fromFuture(firstReceived)\n\t\t\t\t\t\t: empty())\n\t\t\t\t.doOnNext(sent -> sentCount.incrementAndGet());\n\n\t\treturned.doOnNext(received -> {\n\t\t\tbyte[] dataReceived = new byte[received.limit()];\n\t\t\treceived.get(dataReceived);\n\t\t\treceivedAll.add(dataReceived);\n\t\t\tassertThat(receivedAll.size()).isEqualTo(sentCount.get());\n\t\t\tfirstReceived.complete(received);\n\t\t\tcountDownLatch.countDown();\n\t\t}).subscribe();\n\n\t\tcountDownLatch.await();\n\n\t\tassertThat(receivedAll).containsExactly(new byte[]{1,2,3}, new byte[]{4,5,6});\n\t}\n\n\tprivate static ByteBuffer fromByteArray(byte[] data){\n\t\treturn ByteBuffer.wrap(data);\n\t}\n\n\t@Configuration\n\tpublic static class TestConfiguration{\n\n\t\t@Bean\n\t\tpublic ReactiveWebServerFactory reactiveWebServerFactory(){\n\t\t\treturn new NettyReactiveWebServerFactory();\n\t\t}\n\t}\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/CompressionTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class CompressionTest extends reactivefeign.CompressionTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return RestTemplateFakeReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/ConnectionTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ConnectionTimeoutTest extends reactivefeign.ConnectionTimeoutTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return RestTemplateFakeReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/ContractTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ContractTest extends reactivefeign.ContractTest {\n\n  @Override\n  protected <T> ReactiveFeign.Builder<T> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/DefaultMethodTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class DefaultMethodTest extends reactivefeign.DefaultMethodTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n\n  @Override\n  protected <API> ReactiveFeign.Builder<API> builder(Class<API> apiClass) {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return RestTemplateFakeReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/LoggerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class LoggerTest extends reactivefeign.LoggerTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/NotFoundTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class NotFoundTest extends reactivefeign.NotFoundTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/ReactivityTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport org.awaitility.core.ConditionTimeoutException;\nimport org.junit.Before;\nimport org.junit.Test;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\npublic class ReactivityTest extends reactivefeign.ReactivityTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n\n  @Test(expected = ConditionTimeoutException.class)\n  @Override\n  public void shouldRunReactively() throws JsonProcessingException {\n    super.shouldRunReactively();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/ReadTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ReadTimeoutTest extends reactivefeign.ReadTimeoutTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return RestTemplateFakeReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/RequestInterceptorTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class RequestInterceptorTest extends reactivefeign.RequestInterceptorTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/RetryingTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class RetryingTest extends reactivefeign.RetryingTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/SmokeTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class SmokeTest extends reactivefeign.SmokeTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/StatusHandlerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.resttemplate.client.RestTemplateFakeReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class StatusHandlerTest extends reactivefeign.StatusHandlerTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return RestTemplateFakeReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/client/RestTemplateFakeReactiveFeign.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate.client;\n\nimport org.apache.http.impl.client.HttpClientBuilder;\nimport org.springframework.http.client.HttpComponentsClientHttpRequestFactory;\nimport org.springframework.web.client.RestTemplate;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\n\nimport static java.util.Optional.ofNullable;\n\n/**\n * {@link RestTemplate} based implementation\n *\n * @author Sergii Karpenko\n */\npublic class RestTemplateFakeReactiveFeign {\n\n  public static <T> ReactiveFeign.Builder<T> builder() {\n    return new ReactiveFeign.Builder<T>(){\n\n      {\n        clientFactory(methodMetadata -> new RestTemplateFakeReactiveHttpClient(\n                methodMetadata, new RestTemplate(), false));\n      }\n\n      @Override\n      public ReactiveFeign.Builder<T> options(ReactiveOptions options) {\n        HttpComponentsClientHttpRequestFactory requestFactory =\n                new HttpComponentsClientHttpRequestFactory(\n                        HttpClientBuilder.create().build());\n        if (options.getConnectTimeoutMillis() != null) {\n          requestFactory.setConnectTimeout(options.getConnectTimeoutMillis().intValue());\n        }\n        if (options.getReadTimeoutMillis() != null) {\n          requestFactory.setReadTimeout(options.getReadTimeoutMillis().intValue());\n        }\n\n        this.clientFactory(methodMetadata -> {\n          boolean acceptGzip = ofNullable(options.isTryUseCompression()).orElse(false);\n          return new RestTemplateFakeReactiveHttpClient(\n                  methodMetadata, new RestTemplate(requestFactory), acceptGzip);\n        });\n\n        return this;\n      }\n    };\n  }\n}\n\n\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/resttemplate/client/RestTemplateFakeReactiveHttpClient.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.resttemplate.client;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport feign.MethodMetadata;\nimport org.reactivestreams.Publisher;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpEntity;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.ResponseEntity;\nimport org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;\nimport org.springframework.util.LinkedMultiValueMap;\nimport org.springframework.util.MultiValueMap;\nimport org.springframework.web.client.HttpStatusCodeException;\nimport org.springframework.web.client.ResourceAccessException;\nimport org.springframework.web.client.RestTemplate;\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactivefeign.client.ReadTimeoutException;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.net.SocketTimeoutException;\nimport java.util.List;\nimport java.util.Map;\n\nimport static feign.Util.resolveLastTypeParameter;\nimport static org.springframework.core.ParameterizedTypeReference.forType;\n\npublic class RestTemplateFakeReactiveHttpClient implements ReactiveHttpClient {\n\n  private final RestTemplate restTemplate;\n  private final boolean acceptGzip;\n  private final Type returnPublisherType;\n  private final ParameterizedTypeReference<Object> returnActualType;\n\n  RestTemplateFakeReactiveHttpClient(MethodMetadata methodMetadata,\n      RestTemplate restTemplate,\n      boolean acceptGzip) {\n    this.restTemplate = restTemplate;\n    this.acceptGzip = acceptGzip;\n\n    ObjectMapper mapper = new ObjectMapper();\n    mapper.registerModule(new JavaTimeModule());\n\n    MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();\n    converter.setObjectMapper(mapper);\n    restTemplate.getMessageConverters().add(0, converter);\n\n\n    final Type returnType = methodMetadata.returnType();\n    returnPublisherType = ((ParameterizedType) returnType).getRawType();\n    returnActualType = forType(\n        resolveLastTypeParameter(returnType, (Class<?>) returnPublisherType));\n  }\n\n  @Override\n  public Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request) {\n\n    Mono<Object> bodyMono;\n    if (request.body() instanceof Mono) {\n      bodyMono = ((Mono<Object>) request.body());\n    } else if (request.body() instanceof Flux) {\n      bodyMono = ((Flux) request.body()).collectList();\n    } else {\n      bodyMono = Mono.just(request.body());\n    }\n    bodyMono = bodyMono.switchIfEmpty(Mono.just(new byte[0]));\n\n    return bodyMono.<ReactiveHttpResponse>flatMap(body -> {\n      MultiValueMap<String, String> headers = new LinkedMultiValueMap<>(request.headers());\n      if (acceptGzip) {\n        headers.add(\"Accept-Encoding\", \"gzip\");\n      }\n\n      ResponseEntity response =\n              restTemplate.exchange(request.uri().toString(), HttpMethod.valueOf(request.method()),\n                      new HttpEntity<>(body, headers), responseType());\n\n      return Mono.just(new FakeReactiveHttpResponse(response, returnPublisherType));\n    })\n            .onErrorMap(ex -> ex instanceof ResourceAccessException\n                                && ex.getCause() instanceof SocketTimeoutException,\n                    ReadTimeoutException::new)\n            .onErrorResume(HttpStatusCodeException.class,\n                    ex -> Mono.just(new ErrorReactiveHttpResponse(ex)));\n  }\n\n  private ParameterizedTypeReference<Object> responseType(){\n    if (returnPublisherType == Mono.class) {\n      return returnActualType;\n    } else {\n      return forType(new ParameterizedType() {\n        @Override\n        public Type[] getActualTypeArguments() {\n          return new Type[] {returnActualType.getType()};\n        }\n\n        @Override\n        public Type getRawType() {\n          return List.class;\n        }\n\n        @Override\n        public Type getOwnerType() {\n          return null;\n        }\n      });\n    }\n  }\n\n  private static class FakeReactiveHttpResponse implements ReactiveHttpResponse{\n\n    private final ResponseEntity response;\n    private final Type returnPublisherType;\n\n    private FakeReactiveHttpResponse(ResponseEntity response, Type returnPublisherType) {\n      this.response = response;\n      this.returnPublisherType = returnPublisherType;\n    }\n\n    @Override\n    public int status() {\n      return response.getStatusCodeValue();\n    }\n\n    @Override\n    public Map<String, List<String>> headers() {\n      return response.getHeaders();\n    }\n\n    @Override\n    public Publisher<Object> body() {\n      if (returnPublisherType == Mono.class) {\n        return Mono.justOrEmpty(response.getBody());\n      } else {\n        return Flux.fromIterable((List)response.getBody());\n      }\n    }\n\n    @Override\n    public Mono<byte[]> bodyData() {\n      return Mono.just(new byte[0]);\n    }\n  }\n\n  private static class ErrorReactiveHttpResponse implements ReactiveHttpResponse{\n\n    private final HttpStatusCodeException ex;\n\n    private ErrorReactiveHttpResponse(HttpStatusCodeException ex) {\n      this.ex = ex;\n    }\n\n    @Override\n    public int status() {\n      return ex.getStatusCode().value();\n    }\n\n    @Override\n    public Map<String, List<String>> headers() {\n      return ex.getResponseHeaders();\n    }\n\n    @Override\n    public Publisher<Object> body() {\n      return Mono.empty();\n    }\n\n    @Override\n    public Mono<byte[]> bodyData() {\n      return Mono.just(ex.getResponseBodyAsByteArray());\n    }\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApi.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.testcase;\n\nimport feign.Headers;\nimport feign.Param;\nimport feign.RequestLine;\nimport reactivefeign.testcase.domain.Bill;\nimport reactivefeign.testcase.domain.Flavor;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.Mixin;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\n/**\n * API of an iceream web service.\n *\n * @author Sergii Karpenko\n */\n@Headers({\"Accept: application/json\"})\npublic interface IcecreamServiceApi {\n\n  RuntimeException RUNTIME_EXCEPTION = new RuntimeException(\"tests exception\");\n\n  @RequestLine(\"GET /icecream/flavors\")\n  Flux<Flavor> getAvailableFlavors();\n\n  @RequestLine(\"GET /icecream/mixins\")\n  Flux<Mixin> getAvailableMixins();\n\n  @RequestLine(\"POST /icecream/orders\")\n  @Headers(\"Content-Type: application/json\")\n  Mono<Bill> makeOrder(IceCreamOrder order);\n\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  Mono<IceCreamOrder> findOrder(@Param(\"orderId\") int orderId);\n\n  @RequestLine(\"POST /icecream/bills/pay\")\n  @Headers(\"Content-Type: application/json\")\n  Mono<Void> payBill(Bill bill);\n\n  default Mono<IceCreamOrder> findFirstOrder() {\n    return findOrder(1);\n  }\n\n  default Mono<IceCreamOrder> throwsException() {\n    throw RUNTIME_EXCEPTION;\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApiBroken.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.testcase;\n\nimport feign.Headers;\nimport feign.Param;\nimport feign.RequestLine;\nimport reactivefeign.ReactiveContract;\nimport reactivefeign.testcase.domain.Bill;\nimport reactivefeign.testcase.domain.Flavor;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.Mixin;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.Collection;\n\n/**\n * API of an iceream web service with one method that doesn't returns {@link Mono} or {@link Flux}\n * and violates {@link ReactiveContract}s rules.\n *\n * @author Sergii Karpenko\n */\npublic interface IcecreamServiceApiBroken {\n\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  Mono<IceCreamOrder> findOrder(@Param(\"orderId\") int orderId);\n\n  /**\n   * Method that doesn't respects contract.\n   */\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  IceCreamOrder findOrderBlocking(@Param(\"orderId\") int orderId);\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/IcecreamServiceApiBrokenByCopy.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.testcase;\n\nimport feign.Headers;\nimport feign.Param;\nimport feign.RequestLine;\nimport reactivefeign.ReactiveContract;\nimport reactivefeign.testcase.domain.Bill;\nimport reactivefeign.testcase.domain.Flavor;\nimport reactivefeign.testcase.domain.IceCreamOrder;\nimport reactivefeign.testcase.domain.Mixin;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.nio.ByteBuffer;\nimport java.util.Collection;\n\n/**\n * API of an iceream web service with one method that returns {@link Mono} or {@link Flux} of byte array\n * and violates {@link ReactiveContract}s rules.\n *\n * @author Sergii Karpenko\n */\npublic interface IcecreamServiceApiBrokenByCopy {\n\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  Mono<ByteBuffer> findOrder(@Param(\"orderId\") int orderId);\n\n  /**\n   * Method that doesn't respects contract.\n   */\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  Mono<byte[]> findOrderCopy(@Param(\"orderId\") int orderId);\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Bill.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.testcase.domain;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Bill for consumed ice cream.\n */\npublic class Bill {\n  private static final Map<Integer, Float> PRICES = new HashMap<>();\n\n  static {\n    PRICES.put(1, (float) 2.00); // two euros for one ball (expensive!)\n    PRICES.put(3, (float) 2.85); // 2.85€ for 3 balls\n    PRICES.put(5, (float) 4.30); // 4.30€ for 5 balls\n    PRICES.put(7, (float) 5); // only five euros for seven balls! Wow\n  }\n\n  private static final float MIXIN_PRICE = (float) 0.6; // price per mixin\n\n  private Float price;\n\n  public Bill() {}\n\n  public Bill(final Float price) {\n    this.price = price;\n  }\n\n  public Float getPrice() {\n    return price;\n  }\n\n  public void setPrice(final Float price) {\n    this.price = price;\n  }\n\n  /**\n   * Makes a bill from an order.\n   *\n   * @param order ice cream order\n   * @return bill\n   */\n  public static Bill makeBill(final IceCreamOrder order) {\n    int nbBalls = order.getBalls().values().stream().mapToInt(Integer::intValue)\n        .sum();\n    Float price = PRICES.get(nbBalls) + order.getMixins().size() * MIXIN_PRICE;\n    return new Bill(price);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Flavor.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.testcase.domain;\n\n/**\n * Ice cream flavors.\n */\npublic enum Flavor {\n  STRAWBERRY, CHOCOLATE, BANANA, PISTACHIO, MELON, VANILLA\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/IceCreamOrder.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.testcase.domain;\n\nimport java.time.Instant;\nimport java.util.*;\n\n/**\n * Give me some ice-cream! :p\n */\npublic class IceCreamOrder {\n  private static Random random = new Random();\n\n  private int id; // order id\n  private Map<Flavor, Integer> balls; // how much balls of flavor\n  private Set<Mixin> mixins; // and some mixins ...\n  private Instant orderTimestamp; // and give it to me right now !\n\n  IceCreamOrder() {}\n\n  IceCreamOrder(int id) {\n    this(id, Instant.now());\n  }\n\n  IceCreamOrder(int id, final Instant orderTimestamp) {\n    this.id = id;\n    this.balls = new HashMap<>();\n    this.mixins = new HashSet<>();\n    this.orderTimestamp = orderTimestamp;\n  }\n\n  IceCreamOrder addBall(final Flavor ballFlavor) {\n    final Integer ballCount = balls.containsKey(ballFlavor)\n        ? balls.get(ballFlavor) + 1\n        : 1;\n    balls.put(ballFlavor, ballCount);\n    return this;\n  }\n\n  IceCreamOrder addMixin(final Mixin mixin) {\n    mixins.add(mixin);\n    return this;\n  }\n\n  IceCreamOrder withOrderTimestamp(final Instant orderTimestamp) {\n    this.orderTimestamp = orderTimestamp;\n    return this;\n  }\n\n  public int getId() {\n    return id;\n  }\n\n  public Map<Flavor, Integer> getBalls() {\n    return balls;\n  }\n\n  public Set<Mixin> getMixins() {\n    return mixins;\n  }\n\n  public Instant getOrderTimestamp() {\n    return orderTimestamp;\n  }\n\n  @Override\n  public String toString() {\n    return \"IceCreamOrder{\" + \" id=\" + id + \", balls=\" + balls + \", mixins=\" + mixins\n        + \", orderTimestamp=\" + orderTimestamp + '}';\n  }\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/Mixin.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.testcase.domain;\n\n/**\n * Ice cream mix-ins.\n */\npublic enum Mixin {\n  COOKIES, MNMS, CHOCOLATE_SIROP, STRAWBERRY_SIROP, NUTS, RAINBOW\n}\n"
  },
  {
    "path": "feign-reactor-core/src/test/java/reactivefeign/testcase/domain/OrderGenerator.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.testcase.domain;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\n/**\n * Generator of random ice cream orders.\n */\npublic class OrderGenerator {\n  private static final int[] BALLS_NUMBER = {1, 3, 5, 7};\n  private static final int[] MIXIN_NUMBER = {1, 2, 3};\n\n  private static final Random random = new Random();\n\n  public IceCreamOrder generate(int id) {\n    final IceCreamOrder order = new IceCreamOrder(id);\n    final int nbBalls = peekBallsNumber();\n    final int nbMixins = peekMixinNumber();\n\n    IntStream.rangeClosed(1, nbBalls).mapToObj(i -> this.peekFlavor())\n        .forEach(order::addBall);\n\n    IntStream.rangeClosed(1, nbMixins).mapToObj(i -> this.peekMixin())\n        .forEach(order::addMixin);\n\n    return order;\n  }\n\n  public Collection<IceCreamOrder> generateRange(int n) {\n    Instant now = Instant.now();\n\n    List<Instant> orderTimestamps = IntStream.range(0, n)\n        .mapToObj(minutes -> now.minus(minutes, ChronoUnit.MINUTES))\n        .collect(Collectors.toList());\n\n    return IntStream.range(0, n)\n        .mapToObj(\n            i -> this.generate(i).withOrderTimestamp(orderTimestamps.get(i)))\n        .collect(Collectors.toList());\n  }\n\n  private int peekBallsNumber() {\n    return BALLS_NUMBER[random.nextInt(BALLS_NUMBER.length)];\n  }\n\n  private int peekMixinNumber() {\n    return MIXIN_NUMBER[random.nextInt(MIXIN_NUMBER.length)];\n  }\n\n  private Flavor peekFlavor() {\n    return Flavor.values()[random.nextInt(Flavor.values().length)];\n  }\n\n  private Mixin peekMixin() {\n    return Mixin.values()[random.nextInt(Mixin.values().length)];\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.github.reactivefeign</groupId>\n        <artifactId>feign-reactor</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>feign-reactor-jetty</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.github.reactivefeign</groupId>\n            <artifactId>feign-reactor-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.eclipse.jetty</groupId>\n            <artifactId>jetty-reactive-httpclient</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.kptfh.reactivejson</groupId>\n            <artifactId>json-reactor</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>com.fasterxml.jackson.datatype</groupId>\n            <artifactId>jackson-datatype-jsr310</artifactId>\n        </dependency>\n\n        <!-- Tests -->\n        <dependency>\n            <groupId>io.github.reactivefeign</groupId>\n            <artifactId>feign-reactor-core</artifactId>\n            <version>1.0.0-SNAPSHOT</version>\n            <type>test-jar</type>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.projectreactor</groupId>\n            <artifactId>reactor-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.hamcrest</groupId>\n            <artifactId>hamcrest-library</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.tomakehurst</groupId>\n            <artifactId>wiremock</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.logging.log4j</groupId>\n            <artifactId>log4j-slf4j-impl</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.mockito</groupId>\n            <artifactId>mockito-all</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.awaitility</groupId>\n            <artifactId>awaitility</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <exclusions>\n                <exclusion>\n                    <artifactId>spring-boot-starter-logging</artifactId>\n                    <groupId>org.springframework.boot</groupId>\n                </exclusion>\n            </exclusions>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "feign-reactor-jetty/src/main/java/reactivefeign/jetty/JettyReactiveFeign.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport com.fasterxml.jackson.core.async_.JsonFactory;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\nimport org.eclipse.jetty.client.HttpClient;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.jetty.client.JettyReactiveHttpClient;\n\n/**\n * Reactive Jetty client based implementation of reactive Feign\n *\n * @author Sergii Karpenko\n */\npublic class JettyReactiveFeign {\n\n  public static <T> Builder<T> builder() {\n      try {\n          HttpClient httpClient = new HttpClient();\n          httpClient.start();\n          ObjectMapper objectMapper = new ObjectMapper();\n          objectMapper.registerModule(new JavaTimeModule());\n          return new Builder<>(httpClient, new JsonFactory(), objectMapper);\n      } catch (Exception e) {\n          throw new RuntimeException(e);\n      }\n\n  }\n\n  public static <T> Builder<T> builder(HttpClient httpClient, JsonFactory jsonFactory, ObjectMapper objectMapper) {\n        return new Builder<>(httpClient, jsonFactory, objectMapper);\n    }\n\n  public static class Builder<T> extends ReactiveFeign.Builder<T> {\n\n      protected HttpClient httpClient;\n      protected JsonFactory jsonFactory;\n      private ObjectMapper objectMapper;\n      protected ReactiveOptions options;\n\n      protected Builder(HttpClient httpClient, JsonFactory jsonFactory, ObjectMapper objectMapper) {\n          setHttpClient(httpClient, jsonFactory, objectMapper);\n          this.jsonFactory = jsonFactory;\n          this.objectMapper = objectMapper;\n      }\n\n      @Override\n      public Builder<T> options(ReactiveOptions options) {\n          if (options.getConnectTimeoutMillis() != null) {\n              httpClient.setConnectTimeout(options.getConnectTimeoutMillis());\n          }\n          if (options.getReadTimeoutMillis() != null) {\n              setHttpClient(httpClient, jsonFactory, objectMapper);\n          }\n          this.options = options;\n          return this;\n      }\n\n      protected void setHttpClient(HttpClient httpClient, JsonFactory jsonFactory, ObjectMapper objectMapper){\n          this.httpClient = httpClient;\n          clientFactory(methodMetadata -> {\n              JettyReactiveHttpClient jettyClient = JettyReactiveHttpClient.jettyClient(methodMetadata, httpClient, jsonFactory, objectMapper);\n              if (options != null && options.getReadTimeoutMillis() != null) {\n                  jettyClient.setRequestTimeout(options.getReadTimeoutMillis());\n              }\n              return jettyClient;\n          });\n      }\n  }\n}\n\n\n"
  },
  {
    "path": "feign-reactor-jetty/src/main/java/reactivefeign/jetty/client/JettyReactiveHttpClient.java",
    "content": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reactivefeign.jetty.client;\n\nimport com.fasterxml.jackson.core.async_.JsonFactory;\nimport com.fasterxml.jackson.core.util.ByteArrayBuilder;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.ObjectReader;\nimport com.fasterxml.jackson.databind.ObjectWriter;\nimport feign.MethodMetadata;\nimport org.eclipse.jetty.client.HttpClient;\nimport org.eclipse.jetty.client.api.Request;\nimport org.eclipse.jetty.http.HttpFields;\nimport org.eclipse.jetty.reactive.client.ContentChunk;\nimport org.eclipse.jetty.reactive.client.ReactiveRequest;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactivefeign.client.ReadTimeoutException;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.io.UncheckedIOException;\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.util.concurrent.TimeUnit;\n\nimport static feign.Util.resolveLastTypeParameter;\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static java.util.Collections.singletonList;\nimport static org.eclipse.jetty.http.HttpHeader.ACCEPT;\nimport static org.eclipse.jetty.http.HttpHeader.CONTENT_TYPE;\nimport static reactivefeign.jetty.utils.ProxyPostProcessor.postProcess;\nimport static reactivefeign.utils.FeignUtils.getBodyActualType;\n\n/**\n * Uses reactive Jetty client to execute http requests\n * @author Sergii Karpenko\n */\npublic class JettyReactiveHttpClient implements ReactiveHttpClient {\n\n\tpublic static final String APPLICATION_OCTET_STREAM = \"application/octet-stream\";\n\tpublic static final String TEXT = \"text/plain\";\n\tpublic static final String TEXT_UTF_8 = TEXT+\";charset=utf-8\";\n\n\tpublic static final String APPLICATION_JSON = \"application/json\";\n\tpublic static final String APPLICATION_JSON_UTF_8 = APPLICATION_JSON+\";charset=utf-8\";\n\tpublic static final String APPLICATION_STREAM_JSON = \"application/stream+json\";\n\tpublic static final String APPLICATION_STREAM_JSON_UTF_8 = APPLICATION_STREAM_JSON+\";charset=utf-8\";\n\n\n\tprivate static final byte[] NEWLINE_SEPARATOR = {'\\n'};\n\n\tprivate final HttpClient httpClient;\n\tprivate final Class bodyActualClass;\n\tprivate final Class returnPublisherClass;\n\tprivate final Class returnActualClass;\n\tprivate final JsonFactory jsonFactory;\n\tprivate final ObjectWriter bodyWriter;\n\tprivate final ObjectReader responseReader;\n\tprivate long requestTimeout = -1;\n\n\tpublic static JettyReactiveHttpClient jettyClient(\n\t\t\tMethodMetadata methodMetadata,\n\t\t\tHttpClient httpClient,\n\t\t\tJsonFactory jsonFactory, ObjectMapper objectMapper) {\n\n\t\tfinal Type returnType = methodMetadata.returnType();\n\t\tClass returnPublisherType = (Class)((ParameterizedType) returnType).getRawType();\n\t\tClass returnActualType = getClass(resolveLastTypeParameter(returnType, returnPublisherType));\n\t\tClass bodyActualType = getClass(getBodyActualType(methodMetadata.bodyType()));\n\n\t\treturn new JettyReactiveHttpClient(httpClient,\n\t\t\t\tbodyActualType, returnPublisherType, returnActualType,\n\t\t\t\tjsonFactory,\n\t\t\t\tobjectMapper.writerFor(bodyActualType),\n\t\t\t\tobjectMapper.readerFor(returnActualType));\n\t}\n\n\tpublic JettyReactiveHttpClient(HttpClient httpClient,\n\t\t\t\t\t\t\t\t   Class bodyActualClass, Class returnPublisherClass, Class returnActualClass,\n\t\t\t\t\t\t\t\t   JsonFactory jsonFactory, ObjectWriter bodyWriter, ObjectReader responseReader) {\n\t\tthis.httpClient = httpClient;\n\t\tthis.bodyActualClass = bodyActualClass;\n\t\tthis.returnPublisherClass = returnPublisherClass;\n\t\tthis.returnActualClass = returnActualClass;\n\t\tthis.jsonFactory = jsonFactory;\n\t\tthis.bodyWriter = bodyWriter;\n\t\tthis.responseReader = responseReader;\n\t}\n\n\tpublic JettyReactiveHttpClient setRequestTimeout(long timeoutInMillis){\n\t\tthis.requestTimeout = timeoutInMillis;\n\t\treturn this;\n\t}\n\n\t@Override\n\tpublic Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request) {\n\t\tRequest jettyRequest = httpClient.newRequest(request.uri()).method(request.method());\n\t\tsetUpHeaders(request, jettyRequest.getHeaders());\n\t\tif(requestTimeout > 0){\n\t\t\tjettyRequest.timeout(requestTimeout, TimeUnit.MILLISECONDS);\n\t\t}\n\n\t\tReactiveRequest.Builder requestBuilder = ReactiveRequest.newBuilder(jettyRequest);\n\t\tif(bodyActualClass != null){\n\t\t\tReactiveRequest.Content content = provideBody(request);\n\t\t\trequestBuilder.content(content);\n\t\t\tjettyRequest.getHeaders().put(CONTENT_TYPE.asString(), singletonList(content.getContentType()));\n\t\t}\n\n\t\treturn Mono.<ReactiveHttpResponse>from(requestBuilder.build().response((response, content) -> Mono.just(\n\t\t\t\tnew JettyReactiveHttpResponse(response.getResponse(),\n\t\t\t\t\t\tpostProcess(content,\n\t\t\t\t\t\t\t\t(contentChunk, throwable) -> {\n\t\t\t\t\t\t\t\t\tif(throwable != null){\n\t\t\t\t\t\t\t\t\t\tcontentChunk.callback.failed(throwable);\n\t\t\t\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t\t\t\tcontentChunk.callback.succeeded();\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t}),\n\t\t\t\t\t\treturnPublisherClass, returnActualClass,\n\t\t\t\t\t\tjsonFactory, responseReader))\n\n\n\t\t)).onErrorMap(ex -> ex instanceof java.util.concurrent.TimeoutException,\n\t\t\t\tReadTimeoutException::new);\n\t}\n\n\tprotected void setUpHeaders(ReactiveHttpRequest request, HttpFields httpHeaders) {\n\t\trequest.headers().forEach(httpHeaders::put);\n\n\t\tString acceptHeader;\n\t\tif(CharSequence.class.isAssignableFrom(returnActualClass) && returnPublisherClass == Mono.class){\n\t\t\tacceptHeader = TEXT;\n\t\t}\n\t\telse if(returnActualClass == ByteBuffer.class || returnActualClass == byte[].class){\n\t\t\tacceptHeader = APPLICATION_OCTET_STREAM;\n\t\t}\n\t\telse if(returnPublisherClass == Mono.class){\n\t\t\tacceptHeader = APPLICATION_JSON;\n\t\t}\n\t\telse {\n\t\t\tacceptHeader = APPLICATION_STREAM_JSON;\n\t\t}\n\t\thttpHeaders.put(ACCEPT.asString(), singletonList(acceptHeader));\n\t}\n\n\tprotected ReactiveRequest.Content provideBody(ReactiveHttpRequest request) {\n\t\tPublisher<ContentChunk> bodyPublisher;\n\t\tString contentType;\n\t\tif(request.body() instanceof Mono){\n\t\t\tif(bodyActualClass == ByteBuffer.class){\n\t\t\t\tbodyPublisher = ((Mono)request.body()).map(this::toByteBufferChunk);\n\t\t\t\tcontentType = APPLICATION_OCTET_STREAM;\n\t\t\t}\n\t\t\telse if(bodyActualClass == byte[].class){\n\t\t\t\tbodyPublisher = Flux.from(request.body()).map(this::toByteArrayChunk);\n\t\t\t\tcontentType = APPLICATION_OCTET_STREAM;\n\t\t\t}\n\t\t\telse if (CharSequence.class.isAssignableFrom(bodyActualClass)){\n\t\t\t\tbodyPublisher = Flux.from(request.body()).map(this::toCharSequenceChunk);\n\t\t\t\tcontentType = TEXT_UTF_8;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbodyPublisher = Flux.from(request.body()).map(data -> toJsonChunk(data, false));\n\t\t\t\tcontentType = APPLICATION_JSON_UTF_8;\n\t\t\t}\n\n\t\t} else {\n\t\t\tif(bodyActualClass == ByteBuffer.class){\n\t\t\t\tbodyPublisher = Flux.from(request.body()).map(this::toByteBufferChunk);\n\t\t\t\tcontentType = APPLICATION_OCTET_STREAM;\n\t\t\t}\n\t\t\telse if(bodyActualClass == byte[].class){\n\t\t\t\tbodyPublisher = Flux.from(request.body()).map(this::toByteArrayChunk);\n\t\t\t\tcontentType = APPLICATION_OCTET_STREAM;\n\t\t\t}\n\t\t\telse {\n\t\t\t\tbodyPublisher = Flux.from(request.body()).map(data -> toJsonChunk(data, true));\n\t\t\t\tcontentType = APPLICATION_STREAM_JSON_UTF_8;\n\t\t\t}\n\t\t}\n\n\t\treturn ReactiveRequest.Content.fromPublisher(bodyPublisher, contentType);\n\t}\n\n\tprotected ContentChunk toByteBufferChunk(Object data){\n\t\treturn new ContentChunk((ByteBuffer)data);\n\t}\n\n\tprotected ContentChunk toByteArrayChunk(Object data){\n\t\treturn new ContentChunk(ByteBuffer.wrap((byte[])data));\n\t}\n\n\tprotected ContentChunk toCharSequenceChunk(Object data){\n\t\tCharBuffer charBuffer = CharBuffer.wrap((CharSequence) data);\n\t\tByteBuffer byteBuffer = UTF_8.encode(charBuffer);\n\t\treturn new ContentChunk(byteBuffer);\n\t}\n\n\tprotected ContentChunk toJsonChunk(Object data, boolean stream){\n\t\ttry {\n\t\t\tByteArrayBuilder byteArrayBuilder = new ByteArrayBuilder();\n\t\t\tbodyWriter.writeValue(byteArrayBuilder, data);\n\t\t\tif(stream) {\n\t\t\t\tbyteArrayBuilder.write(NEWLINE_SEPARATOR);\n\t\t\t}\n\t\t\tByteBuffer buffer = ByteBuffer.wrap(byteArrayBuilder.toByteArray());\n\t\t\treturn new ContentChunk(buffer);\n\t\t} catch (java.io.IOException e) {\n\t\t\tthrow new UncheckedIOException(e);\n\t\t}\n\t}\n\n\tpublic static Class getClass(Type type){\n\t\treturn (Class)(type instanceof ParameterizedType\n\t\t\t\t? ((ParameterizedType) type).getRawType() : type);\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/main/java/reactivefeign/jetty/client/JettyReactiveHttpResponse.java",
    "content": "package reactivefeign.jetty.client;\n\nimport com.fasterxml.jackson.core.async_.JsonFactory;\nimport com.fasterxml.jackson.databind.ObjectReader;\nimport org.eclipse.jetty.client.api.Response;\nimport org.eclipse.jetty.http.HttpField;\nimport org.eclipse.jetty.reactive.client.ContentChunk;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactivejson.ReactorObjectReader;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.io.ByteArrayOutputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.charset.Charset;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport static java.nio.charset.StandardCharsets.UTF_8;\nimport static java.util.Arrays.asList;\nimport static java.util.Optional.ofNullable;\nimport static org.eclipse.jetty.http.HttpHeader.CONTENT_TYPE;\n\nclass JettyReactiveHttpResponse implements ReactiveHttpResponse{\n\n\tpublic static final String CHARSET_DELIMITER = \";charset=\";\n\tprivate final Response clientResponse;\n\tprivate final Publisher<ContentChunk> contentChunks;\n\tprivate final Class returnPublisherType;\n\tprivate Class<?> returnActualClass;\n\tprivate final ObjectReader objectReader;\n\tprivate final JsonFactory jsonFactory;\n\n\tJettyReactiveHttpResponse(Response clientResponse, Publisher<ContentChunk> contentChunks,\n\t\t\t\t\t\t\t  Class returnPublisherType, Class returnActualClass,\n\t\t\t\t\t\t\t  JsonFactory jsonFactory, ObjectReader objectReader) {\n\t\tthis.clientResponse = clientResponse;\n\t\tthis.contentChunks = contentChunks;\n\t\tthis.returnPublisherType = returnPublisherType;\n\t\tthis.returnActualClass = returnActualClass;\n\t\tthis.objectReader = objectReader;\n\t\tthis.jsonFactory = jsonFactory;\n\t}\n\n\t@Override\n\tpublic int status() {\n\t\treturn clientResponse.getStatus();\n\t}\n\n\t@Override\n\tpublic Map<String, List<String>> headers() {\n\t\treturn clientResponse.getHeaders().stream()\n\t\t\t\t.collect(Collectors.toMap(HttpField::getName, field -> asList(field.getValues())));\n\t}\n\n\t@Override\n\tpublic Publisher<?> body() {\n\t\tReactorObjectReader reactorObjectReader = new ReactorObjectReader(jsonFactory);\n\n\t\tFlux<ByteBuffer> content = directContent();\n\n\t\tif(returnActualClass == ByteBuffer.class){\n\t\t\treturn content;\n\t\t} else if(returnActualClass.isAssignableFrom(String.class)\n\t\t\t\t&& returnPublisherType == Mono.class){\n\t\t\tCharset charset = getCharset();\n\t\t\treturn content.map(byteBuffer -> charset.decode(byteBuffer).toString());\n\t\t} else {\n\t\t\tif (returnPublisherType == Mono.class) {\n\t\t\t\treturn reactorObjectReader.read(content, objectReader);\n\t\t\t} else if(returnPublisherType == Flux.class){\n\t\t\t\treturn reactorObjectReader.readElements(content, objectReader);\n\t\t\t} else {\n\t\t\t\tthrow new IllegalArgumentException(\"Unknown returnPublisherType: \" + returnPublisherType);\n\t\t\t}\n\t\t}\n\t}\n\n\tprivate Charset getCharset() {\n\t\treturn ofNullable(clientResponse.getHeaders().get(CONTENT_TYPE.asString()))\n\t\t\t\t.map(header -> {\n\t\t\t\t\tint pos = header.indexOf(CHARSET_DELIMITER);\n\t\t\t\t\tif(pos >= 0){\n\t\t\t\t\t\treturn header.substring(pos + CHARSET_DELIMITER.length());\n\t\t\t\t\t} else {\n\t\t\t\t\t\treturn null;\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t\t.map(Charset::forName)\n\t\t\t\t.orElse(UTF_8);\n\t}\n\n\tprivate Flux<ByteBuffer> directContent() {\n\t\treturn Flux.from(contentChunks).map(contentChunk -> contentChunk.buffer.slice());\n\t}\n\n\t@Override\n\tpublic Mono<byte[]> bodyData() {\n\t\treturn joinChunks();\n\t}\n\n\tprivate Mono<byte[]> joinChunks() {\n\t\treturn directContent().reduce(new ByteArrayOutputStream(), (baos, byteBuffer) -> {\n\t\t\tfor(int i = byteBuffer.position(), limit = byteBuffer.limit(); i < limit; i++){\n\t\t\t\tbaos.write(byteBuffer.get(i));\n\t\t\t}\n\t\t\treturn baos;\n\t\t}).map(ByteArrayOutputStream::toByteArray);\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/main/java/reactivefeign/jetty/utils/ProxyPostProcessor.java",
    "content": "package reactivefeign.jetty.utils;\n\nimport org.eclipse.jetty.reactive.client.internal.AbstractSingleProcessor;\nimport org.reactivestreams.Publisher;\nimport org.reactivestreams.Subscriber;\n\nimport java.util.function.BiConsumer;\n\npublic class ProxyPostProcessor<I> extends AbstractSingleProcessor<I, I>{\n\n\tprivate final Publisher<I> publisher;\n\tprivate final BiConsumer<I, Throwable> postProcessor;\n\n\tprivate ProxyPostProcessor(Publisher<I> publisher, BiConsumer<I, Throwable> postProcessor) {\n\t\tthis.publisher = publisher;\n\t\tthis.postProcessor = postProcessor;\n\t}\n\n\t@Override\n\tpublic void onNext(I i) {\n\t\ttry {\n\t\t\tdownStreamOnNext(i);\n\t\t\tpostProcessor.accept(i, null);\n\t\t} catch (Throwable err) {\n\t\t\tpostProcessor.accept(i, err);\n\t\t}\n\t}\n\n\t@Override\n\tpublic void subscribe(Subscriber<? super I> s) {\n\t\tpublisher.subscribe(this);\n\t\tsuper.subscribe(s);\n\t}\n\n\tpublic static <I> Publisher<I> postProcess(Publisher<I> publisher, BiConsumer<I, Throwable> postProcessor){\n\t\treturn new ProxyPostProcessor<>(publisher, postProcessor);\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/CompressionTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class CompressionTest extends reactivefeign.CompressionTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return JettyReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/ConnectionTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ConnectionTimeoutTest extends reactivefeign.ConnectionTimeoutTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return JettyReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/ContractTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ContractTest extends reactivefeign.ContractTest {\n\n  @Override\n  protected <T> ReactiveFeign.Builder<T> builder() {\n    return JettyReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/DefaultMethodTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class DefaultMethodTest extends reactivefeign.DefaultMethodTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return JettyReactiveFeign.builder();\n  }\n\n  @Override\n  protected <API> ReactiveFeign.Builder<API> builder(Class<API> apiClass) {\n    return JettyReactiveFeign.builder();\n  }\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return JettyReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/LoggerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class LoggerTest extends reactivefeign.LoggerTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return JettyReactiveFeign.builder();\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/NotFoundTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport org.junit.Test;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class NotFoundTest extends reactivefeign.NotFoundTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return JettyReactiveFeign.builder();\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/ReactivityTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\npublic class ReactivityTest extends reactivefeign.ReactivityTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return JettyReactiveFeign.builder();\n  }\n\n  @Override\n  public void shouldRunReactively() throws JsonProcessingException {\n    super.shouldRunReactively();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/ReadTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ReadTimeoutTest extends reactivefeign.ReadTimeoutTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return JettyReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/RequestInterceptorTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class RequestInterceptorTest extends reactivefeign.RequestInterceptorTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return JettyReactiveFeign.builder();\n  }\n\n  @Override\n  protected Class notAuthorizedException() {\n    return org.eclipse.jetty.client.HttpResponseException.class;\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/RetryingTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class RetryingTest extends reactivefeign.RetryingTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return JettyReactiveFeign.builder();\n  }\n\n  @Test\n  public void shouldFailAsNoMoreRetriesWithBackoff() {\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/SmokeTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class SmokeTest extends reactivefeign.SmokeTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return JettyReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/StatusHandlerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.jetty;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class StatusHandlerTest extends reactivefeign.StatusHandlerTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return JettyReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/java/reactivefeign/jetty/allfeatures/AllFeaturesTest.java",
    "content": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reactivefeign.jetty.allfeatures;\n\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.jetty.JettyReactiveFeign;\n\n/**\n * @author Sergii Karpenko\n *\n * Tests ReactiveFeign in conjunction with WebFlux rest controller.\n */\n@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration.class, ReactiveUserDetailsServiceAutoConfiguration.class})\npublic class AllFeaturesTest extends reactivefeign.allfeatures.AllFeaturesTest {\n\n\t@Override\n\tprotected ReactiveFeign.Builder<reactivefeign.allfeatures.AllFeaturesApi> builder() {\n\t\treturn JettyReactiveFeign.builder();\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-jetty/src/test/resources/log4j2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\">\n    <Appenders>\n        <Console name=\"Console\" target=\"SYSTEM_OUT\">\n            <PatternLayout pattern=\"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n\"/>\n        </Console>\n    </Appenders>\n    <Loggers>\n        <Root level=\"trace\">\n            <AppenderRef ref=\"Console\"/>\n        </Root>\n    </Loggers>\n</Configuration>"
  },
  {
    "path": "feign-reactor-rx2/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.github.reactivefeign</groupId>\n        <artifactId>feign-reactor</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>feign-reactor-rx2</artifactId>\n\n    <properties>\n    </properties>\n\n    <dependencies>\n\n        <dependency>\n            <groupId>io.projectreactor.addons</groupId>\n            <artifactId>reactor-adapter</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.reactivex.rxjava2</groupId>\n            <artifactId>rxjava</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.github.reactivefeign</groupId>\n            <artifactId>feign-reactor-webclient</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <exclusions>\n                <exclusion>\n                    <artifactId>spring-boot-starter-logging</artifactId>\n                    <groupId>org.springframework.boot</groupId>\n                </exclusion>\n            </exclusions>\n        </dependency>\n\n        <!-- Tests -->\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.projectreactor</groupId>\n            <artifactId>reactor-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.assertj</groupId>\n            <artifactId>assertj-core</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.github.tomakehurst</groupId>\n            <artifactId>wiremock</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.awaitility</groupId>\n            <artifactId>awaitility</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>com.google.guava</groupId>\n            <artifactId>guava</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.logging.log4j</groupId>\n            <artifactId>log4j-slf4j-impl</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n    </dependencies>\n\n</project>"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/Rx2Contract.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport feign.Contract;\nimport feign.MethodMetadata;\nimport io.reactivex.Flowable;\nimport io.reactivex.Maybe;\nimport io.reactivex.Observable;\nimport io.reactivex.Single;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.HashSet;\nimport java.util.List;\nimport java.util.Set;\n\nimport static feign.Util.checkNotNull;\nimport static java.util.Arrays.asList;\n\n/**\n * Contract allowing only {@link Mono} and {@link Flux} return type.\n *\n * @author Sergii Karpenko\n */\npublic class Rx2Contract implements Contract {\n\n  public static final Set<Type> RX2_TYPES = new HashSet<>(asList(\n          Flowable.class, Observable.class, Single.class, Maybe.class));\n\n  private final Contract delegate;\n\n  public Rx2Contract(final Contract delegate) {\n    this.delegate = checkNotNull(delegate, \"delegate must not be null\");\n  }\n\n  @Override\n  public List<MethodMetadata> parseAndValidatateMetadata(final Class<?> targetType) {\n    final List<MethodMetadata> methodsMetadata =\n        this.delegate.parseAndValidatateMetadata(targetType);\n\n    for (final MethodMetadata metadata : methodsMetadata) {\n      final Type type = metadata.returnType();\n      if (!isRx2Type(type)) {\n        throw new IllegalArgumentException(String.format(\n            \"Method %s of contract %s doesn't returns rx2 types\",\n            metadata.configKey(), targetType.getSimpleName()));\n      }\n    }\n\n    return methodsMetadata;\n  }\n\n  private boolean isRx2Type(final Type type) {\n    return (type instanceof ParameterizedType)\n        && RX2_TYPES.contains(((ParameterizedType) type).getRawType());\n  }\n\n\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/Rx2ReactiveFeign.java",
    "content": "package reactivefeign.rx2;\n\nimport feign.Contract;\nimport feign.InvocationHandlerFactory;\nimport feign.MethodMetadata;\nimport io.reactivex.*;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.ReactiveRetryPolicy;\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveHttpRequestInterceptor;\nimport reactivefeign.methodhandler.MethodHandlerFactory;\nimport reactivefeign.publisher.FluxPublisherHttpClient;\nimport reactivefeign.publisher.MonoPublisherHttpClient;\nimport reactivefeign.publisher.PublisherHttpClient;\nimport reactivefeign.rx2.client.statushandler.Rx2ReactiveStatusHandler;\nimport reactivefeign.rx2.client.statushandler.Rx2StatusHandler;\nimport reactivefeign.rx2.methodhandler.Rx2MethodHandlerFactory;\nimport reactivefeign.utils.Pair;\nimport reactivefeign.webclient.WebReactiveFeign;\nimport reactivefeign.webclient.client.WebReactiveHttpClient;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\nimport java.util.List;\n\nimport static feign.Util.resolveLastTypeParameter;\nimport static java.util.Optional.ofNullable;\nimport static reactivefeign.utils.FeignUtils.getBodyActualType;\nimport static reactivefeign.utils.FeignUtils.returnPublisherType;\n\n/**\n * @author Sergii Karpenko\n */\npublic class Rx2ReactiveFeign extends ReactiveFeign {\n\n    private Rx2ReactiveFeign(ReactiveFeign.ParseHandlersByName targetToHandlersByName,\n                             InvocationHandlerFactory factory) {\n        super(targetToHandlersByName, factory);\n    }\n\n    public static <T> Builder<T> builder() {\n        return new Builder<>();\n    }\n\n    public static <T> Builder<T> builder(WebClient webClient) {\n        return new Builder<>(webClient);\n    }\n\n    public static class Builder<T> extends WebReactiveFeign.Builder<T> {\n\n        private BackpressureStrategy backpressureStrategy;\n\n        protected Builder() {\n            super();\n        }\n\n        protected Builder(WebClient webClient) {\n            super(webClient);\n        }\n\n        /**\n         * Used to convert {@link Observable}  into {@link Flux}\n         * @param backpressureStrategy\n         */\n        public void setBackpressureStrategy(BackpressureStrategy backpressureStrategy) {\n            this.backpressureStrategy = backpressureStrategy;\n        }\n\n        @Override\n        protected MethodHandlerFactory buildReactiveMethodHandlerFactory() {\n            return new Rx2MethodHandlerFactory(buildReactiveClientFactory(),\n                    backpressureStrategy);\n        }\n\n        @Override\n        public Builder<T> contract(final Contract contract) {\n            this.contract = new Rx2Contract(contract);\n            return this;\n        }\n\n        @Override\n        public ReactiveFeign.Builder<T> addHeaders(List<Pair<String, String>> headers) {\n            super.addHeaders(headers);\n            return this;\n        }\n\n        @Override\n        public Builder<T> requestInterceptor(ReactiveHttpRequestInterceptor requestInterceptor) {\n            super.requestInterceptor(requestInterceptor);\n            return this;\n        }\n\n        @Override\n        public Builder<T> decode404() {\n            super.decode404();\n            return this;\n        }\n\n        public Builder<T> statusHandler(Rx2StatusHandler statusHandler) {\n            super.statusHandler(new Rx2ReactiveStatusHandler(statusHandler));\n            return this;\n        }\n\n        @Override\n        public Builder<T> retryWhen(ReactiveRetryPolicy retryPolicy){\n            super.retryWhen(retryPolicy);\n            return this;\n        }\n\n        @Override\n        public Builder<T> options(final ReactiveOptions options) {\n            super.options(options);\n            return this;\n        }\n\n        protected PublisherHttpClient toPublisher(ReactiveHttpClient reactiveHttpClient, MethodMetadata methodMetadata){\n            Type returnType = returnPublisherType(methodMetadata);\n            if(returnType == Single.class || returnType == Maybe.class){\n                return new MonoPublisherHttpClient(reactiveHttpClient);\n            } else if(returnType == Flowable.class || returnType == Observable.class){\n                return new FluxPublisherHttpClient(reactiveHttpClient);\n            } else {\n                throw new IllegalArgumentException(\"Unknown returnType: \" + returnType);\n            }\n        }\n\n        @Override\n        protected void setWebClient(WebClient webClient){\n            this.webClient = webClient;\n            clientFactory(methodMetadata -> webClient(methodMetadata, webClient));\n        }\n\n        public static WebReactiveHttpClient webClient(MethodMetadata methodMetadata, WebClient webClient) {\n\n            final Type returnType = methodMetadata.returnType();\n            Type returnPublisherType = ((ParameterizedType) returnType).getRawType();\n            ParameterizedTypeReference<Object> returnActualType = ParameterizedTypeReference.forType(\n                    resolveLastTypeParameter(returnType, (Class<?>) returnPublisherType));\n            ParameterizedTypeReference<Object> bodyActualType = ofNullable(\n                    getBodyActualType(methodMetadata.bodyType()))\n                    .map(type -> ParameterizedTypeReference.forType(type))\n                    .orElse(null);\n\n            return new WebReactiveHttpClient(webClient,\n                    bodyActualType, rx2ToReactor(returnPublisherType), returnActualType);\n        }\n\n        private static Class rx2ToReactor(Type type){\n            if(type == Flowable.class){\n                return Flux.class;\n            } else if(type == Observable.class){\n                return Flux.class;\n            } else if(type == Single.class){\n                return Mono.class;\n            } else if(type == Maybe.class){\n                return Mono.class;\n            } else {\n                throw new IllegalArgumentException(\"Unexpected type=\"+type);\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2ReactiveStatusHandler.java",
    "content": "package reactivefeign.rx2.client.statushandler;\n\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactivefeign.client.statushandler.ReactiveStatusHandler;\nimport reactor.core.publisher.Mono;\n\nimport static reactor.adapter.rxjava.RxJava2Adapter.singleToMono;\n\npublic class Rx2ReactiveStatusHandler implements ReactiveStatusHandler {\n\n\tprivate final Rx2StatusHandler statusHandler;\n\n\tpublic Rx2ReactiveStatusHandler(Rx2StatusHandler statusHandler) {\n\t\tthis.statusHandler = statusHandler;\n\t}\n\n\t@Override\n\tpublic boolean shouldHandle(int status) {\n\t\treturn statusHandler.shouldHandle(status);\n\t}\n\n\t@Override\n\tpublic Mono<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response) {\n\t\treturn singleToMono(statusHandler.decode(methodKey, response));\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2StatusHandler.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.client.statushandler;\n\nimport io.reactivex.Single;\nimport reactivefeign.client.ReactiveHttpResponse;\n\n/**\n * @author Sergii Karpenko\n */\npublic interface Rx2StatusHandler {\n\n  boolean shouldHandle(int status);\n\n  Single<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response);\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/client/statushandler/Rx2StatusHandlers.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.client.statushandler;\n\nimport io.reactivex.Single;\nimport reactivefeign.client.ReactiveHttpResponse;\n\nimport java.util.function.BiFunction;\nimport java.util.function.Predicate;\n\npublic class Rx2StatusHandlers {\n\n\n  public static Rx2StatusHandler throwOnStatus(\n          Predicate<Integer> statusPredicate,\n          BiFunction<String, ReactiveHttpResponse, Throwable> errorFunction) {\n    return new Rx2StatusHandler() {\n      @Override\n      public boolean shouldHandle(int status) {\n        return statusPredicate.test(status);\n      }\n\n      @Override\n      public Single<? extends Throwable> decode(String methodKey, ReactiveHttpResponse response) {\n        return Single.just(errorFunction.apply(methodKey, response));\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2MethodHandler.java",
    "content": "package reactivefeign.rx2.methodhandler;\n\nimport io.reactivex.Flowable;\nimport io.reactivex.Maybe;\nimport io.reactivex.Observable;\nimport io.reactivex.Single;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.methodhandler.MethodHandler;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.Type;\nimport static reactor.adapter.rxjava.RxJava2Adapter.*;\n\npublic class Rx2MethodHandler implements MethodHandler {\n\n\tprivate final MethodHandler methodHandler;\n\tprivate final Type returnPublisherType;\n\n\tpublic Rx2MethodHandler(MethodHandler methodHandler, Type returnPublisherType) {\n\t\tthis.methodHandler = methodHandler;\n\t\tthis.returnPublisherType = returnPublisherType;\n\t}\n\n\t@Override\n\t@SuppressWarnings(\"unchecked\")\n\tpublic Object invoke(final Object[] argv) {\n\t\ttry {\n\t\t\tPublisher<Object> publisher = (Publisher<Object>)methodHandler.invoke(argv);\n\t\t\tif(returnPublisherType == Flowable.class){\n\t\t\t\treturn fluxToFlowable((Flux<Object>) publisher);\n\t\t\t} else if(returnPublisherType == Observable.class){\n\t\t\t\treturn fluxToObservable((Flux<Object>) publisher);\n\t\t\t} else if(returnPublisherType == Single.class){\n\t\t\t\treturn monoToSingle((Mono<Object>) publisher);\n\t\t\t} else if(returnPublisherType == Maybe.class){\n\t\t\t\treturn monoToMaybe((Mono<Object>) publisher);\n\t\t\t} else {\n\t\t\t\tthrow new IllegalArgumentException(\"Unexpected returnPublisherType=\"+returnPublisherType.getClass());\n\t\t\t}\n\t\t} catch (Throwable throwable) {\n\t\t\tif(returnPublisherType == Flowable.class){\n\t\t\t\treturn Flowable.error(throwable);\n\t\t\t} else if(returnPublisherType == Observable.class){\n\t\t\t\treturn Observable.error(throwable);\n\t\t\t} else if(returnPublisherType == Single.class){\n\t\t\t\treturn Single.error(throwable);\n\t\t\t} else if(returnPublisherType == Maybe.class){\n\t\t\t\treturn Maybe.error(throwable);\n\t\t\t} else {\n\t\t\t\tthrow new IllegalArgumentException(\"Unexpected returnPublisherType=\"+returnPublisherType.getClass());\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2MethodHandlerFactory.java",
    "content": "package reactivefeign.rx2.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport io.reactivex.BackpressureStrategy;\nimport reactivefeign.methodhandler.DefaultMethodHandler;\nimport reactivefeign.methodhandler.MethodHandler;\nimport reactivefeign.methodhandler.MethodHandlerFactory;\nimport reactivefeign.publisher.PublisherClientFactory;\n\nimport java.lang.reflect.Method;\n\nimport static reactivefeign.utils.FeignUtils.returnPublisherType;\n\npublic class Rx2MethodHandlerFactory implements MethodHandlerFactory {\n\n    private final PublisherClientFactory publisherClientFactory;\n    private final BackpressureStrategy backpressureStrategy;\n\n    public Rx2MethodHandlerFactory(PublisherClientFactory publisherClientFactory,\n                                   BackpressureStrategy backpressureStrategy) {\n        this.publisherClientFactory = publisherClientFactory;\n        this.backpressureStrategy = backpressureStrategy;\n    }\n\n    @Override\n    public MethodHandler create(final Target target, final MethodMetadata metadata) {\n        MethodHandler methodHandler = new Rx2PublisherClientMethodHandler(\n                target, metadata, publisherClientFactory.apply(metadata),\n                backpressureStrategy);\n\n        return new Rx2MethodHandler(methodHandler, returnPublisherType(metadata));\n    }\n\n    @Override\n    public MethodHandler createDefault(Method method) {\n        return new DefaultMethodHandler(method);\n    }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/main/java/reactivefeign/rx2/methodhandler/Rx2PublisherClientMethodHandler.java",
    "content": "package reactivefeign.rx2.methodhandler;\n\nimport feign.MethodMetadata;\nimport feign.Target;\nimport io.reactivex.*;\nimport org.reactivestreams.Publisher;\nimport reactivefeign.methodhandler.PublisherClientMethodHandler;\nimport reactivefeign.publisher.PublisherHttpClient;\nimport reactor.core.publisher.Mono;\n\nimport static reactor.adapter.rxjava.RxJava2Adapter.*;\n\npublic class Rx2PublisherClientMethodHandler extends PublisherClientMethodHandler {\n\n    private final BackpressureStrategy backpressureStrategy;\n\n    public Rx2PublisherClientMethodHandler(\n            Target target, MethodMetadata methodMetadata,\n            PublisherHttpClient publisherClient, BackpressureStrategy backpressureStrategy) {\n        super(target, methodMetadata, publisherClient);\n        this.backpressureStrategy = backpressureStrategy;\n    }\n\n    @Override\n    protected Publisher<Object> body(Object body) {\n        if (body instanceof Flowable) {\n            return flowableToFlux((Flowable<Object>) body);\n        } else if (body instanceof Observable) {\n            return observableToFlux((Observable<Object>) body,  backpressureStrategy);\n        } else if (body instanceof Single) {\n            return singleToMono((Single<Object>) body);\n        } else if (body instanceof Maybe) {\n            return maybeToMono((Maybe<Object>) body);\n        } else {\n            return Mono.just(body);\n        }\n    }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/ContractTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\nimport reactivefeign.rx2.testcase.IcecreamServiceApiBroken;\n\nimport static org.hamcrest.Matchers.containsString;\n\n/**\n * @author Sergii Karpenko\n */\n\npublic class ContractTest {\n\n  @Rule\n  public ExpectedException expectedException = ExpectedException.none();\n\n  protected <T> ReactiveFeign.Builder<T> builder(){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  @Test\n  public void shouldFailOnBrokenContract() {\n\n    expectedException.expect(IllegalArgumentException.class);\n    expectedException.expectMessage(containsString(\"Broken Contract\"));\n\n    this.<IcecreamServiceApi>builder()\n        .contract(targetType -> {\n          throw new IllegalArgumentException(\"Broken Contract\");\n        })\n        .target(IcecreamServiceApi.class, \"http://localhost:8888\");\n  }\n\n  @Test\n  public void shouldFailIfNotReactiveContract() {\n\n    expectedException.expect(IllegalArgumentException.class);\n    expectedException.expectMessage(containsString(\"IcecreamServiceApiBroken#findOrder(int)\"));\n\n    this.<IcecreamServiceApiBroken>builder()\n        .target(IcecreamServiceApiBroken.class, \"http://localhost:8888\");\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/DefaultMethodTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport feign.RequestLine;\nimport io.reactivex.Single;\nimport org.assertj.core.api.Assertions;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\nimport reactivefeign.rx2.testcase.domain.IceCreamOrder;\nimport reactivefeign.rx2.testcase.domain.OrderGenerator;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static reactivefeign.rx2.TestUtils.equalsComparingFieldByFieldRecursivelyRx;\n\n/**\n * @author Sergii Karpenko\n */\npublic class DefaultMethodTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  @Before\n  public void resetServers() {\n    wireMockRule.resetAll();\n  }\n\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  protected <API> ReactiveFeign.Builder<API> builder(Class<API> apiClass){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options){\n    return Rx2ReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n\n  @Test\n  public void shouldProcessDefaultMethodOnProxy() throws JsonProcessingException, InterruptedException {\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(orderStr)));\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    client.findFirstOrder().test()\n            .await()\n            .assertSubscribed()\n            .assertValue(equalsComparingFieldByFieldRecursivelyRx(orderGenerated))\n            .assertNoErrors()\n            .assertComplete();\n  }\n\n  @Test(expected = RuntimeException.class)\n  public void shouldNotWrapException() {\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    client.throwsException().onErrorReturn(\n        throwable -> orderGenerated).blockingGet();\n  }\n\n  @Test\n  public void shouldOverrideEquals() {\n\n    IcecreamServiceApi client = builder(\n        new ReactiveOptions.Builder()\n            .setConnectTimeoutMillis(300)\n            .setReadTimeoutMillis(100).build())\n                .target(IcecreamServiceApi.class,\n                    \"http://localhost:\" + wireMockRule.port());\n\n    IcecreamServiceApi clientWithSameTarget = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n    Assertions.assertThat(client).isEqualTo(clientWithSameTarget);\n\n    IcecreamServiceApi clientWithOtherPort = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + (wireMockRule.port() + 1));\n    Assertions.assertThat(client).isNotEqualTo(clientWithOtherPort);\n\n    OtherApi clientWithOtherInterface = builder(OtherApi.class)\n        .target(OtherApi.class, \"http://localhost:\" + wireMockRule.port());\n    Assertions.assertThat(client).isNotEqualTo(clientWithOtherInterface);\n  }\n\n  interface OtherApi {\n    @RequestLine(\"GET /icecream/flavors\")\n    Single<String> method(String arg);\n  }\n\n  @Test\n  public void shouldOverrideHashcode() {\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    IcecreamServiceApi otherClientWithSameTarget = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    Assertions.assertThat(client.hashCode()).isEqualTo(otherClientWithSameTarget.hashCode());\n  }\n\n  @Test\n  public void shouldOverrideToString() {\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    Assertions.assertThat(client.toString())\n        .isEqualTo(\"HardCodedTarget(type=IcecreamServiceApi, \"\n            + \"url=http://localhost:\" + wireMockRule.port() + \")\");\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/LoggerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\n\npackage reactivefeign.rx2;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport io.reactivex.Single;\nimport org.apache.logging.log4j.Level;\nimport org.apache.logging.log4j.LogManager;\nimport org.apache.logging.log4j.core.Appender;\nimport org.apache.logging.log4j.core.LogEvent;\nimport org.apache.logging.log4j.core.LoggerContext;\nimport org.apache.logging.log4j.core.config.Configuration;\nimport org.apache.logging.log4j.core.config.LoggerConfig;\nimport org.assertj.core.api.Condition;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport org.mockito.ArgumentCaptor;\nimport org.mockito.Mockito;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.client.LoggerReactiveHttpClient;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\nimport reactivefeign.rx2.testcase.domain.Bill;\nimport reactivefeign.rx2.testcase.domain.IceCreamOrder;\nimport reactivefeign.rx2.testcase.domain.OrderGenerator;\n\nimport java.util.List;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static org.mockito.Mockito.*;\n\n/**\n * @author Sergii Karpenko\n */\npublic class LoggerTest {\n\n  public static final String LOGGER_NAME = LoggerReactiveHttpClient.class.getName();\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig()\n          .asynchronousResponseEnabled(true)\n          .dynamicPort());\n\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  protected Appender appender;\n\n  @Test\n  public void shouldLog() throws Exception {\n\n    setLogLevel(Level.TRACE);\n\n    IceCreamOrder order = new OrderGenerator().generate(20);\n    Bill billExpected = Bill.makeBill(order);\n\n    wireMockRule.stubFor(post(urlEqualTo(\"/icecream/orders\"))\n        .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(order)))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(billExpected))));\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class,\n            \"http://localhost:\" + wireMockRule.port());\n\n    Single<Bill> billMono = client.makeOrder(order);\n\n    // no logs before subscription\n    ArgumentCaptor<LogEvent> argumentCaptor = ArgumentCaptor.forClass(LogEvent.class);\n    Mockito.verify(appender, never()).append(argumentCaptor.capture());\n\n    billMono.blockingGet();\n\n    Mockito.verify(appender, times(7)).append(argumentCaptor.capture());\n\n    List<LogEvent> logEvents = argumentCaptor.getAllValues();\n    assertLogEvent(logEvents, 0, Level.DEBUG,\n        \"[IcecreamServiceApi#makeOrder]--->POST http://localhost\");\n    assertLogEvent(logEvents, 1, Level.TRACE,\n        \"[IcecreamServiceApi#makeOrder] REQUEST HEADERS\\n\" +\n            \"Accept:[application/json]\");\n    assertLogEvent(logEvents, 2, Level.TRACE,\n        \"[IcecreamServiceApi#makeOrder] REQUEST BODY\\n\" +\n            \"IceCreamOrder{ id=20, balls=\");\n    assertLogEvent(logEvents, 3, Level.TRACE,\n        \"[IcecreamServiceApi#makeOrder] RESPONSE HEADERS\\n\" +\n            \"Content-Type:application/json\");\n    assertLogEvent(logEvents, 4, Level.DEBUG,\n        \"[IcecreamServiceApi#makeOrder]<--- headers takes\");\n    assertLogEvent(logEvents, 5, Level.TRACE,\n        \"[IcecreamServiceApi#makeOrder] RESPONSE BODY\\n\" +\n            \"reactivefeign.rx2.testcase.domain.Bill\");\n    assertLogEvent(logEvents, 6, Level.DEBUG,\n        \"[IcecreamServiceApi#makeOrder]<--- body takes\");\n  }\n\n  private void assertLogEvent(List<LogEvent> events, int index, Level level, String message) {\n    assertThat(events).element(index)\n        .hasFieldOrPropertyWithValue(\"level\", level)\n        .extracting(\"message\")\n        .extractingResultOf(\"getFormattedMessage\")\n        .have(new Condition<>(o -> ((String) o).contains(message), \"check message\"));\n  }\n\n  @Before\n  public void before() {\n    appender = Mockito.mock(Appender.class);\n    when(appender.getName()).thenReturn(\"TestAppender\");\n    when(appender.isStarted()).thenReturn(true);\n    getLoggerConfig().addAppender(appender, Level.ALL, null);\n  }\n\n  private static void setLogLevel(Level logLevel) {\n    LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);\n    Configuration configuration = loggerContext.getConfiguration();\n    configuration.getLoggerConfig(LOGGER_NAME).setLevel(logLevel);\n    loggerContext.updateLoggers();\n  }\n\n  private static LoggerConfig getLoggerConfig() {\n    LoggerContext loggerContext = (LoggerContext) LogManager.getContext(false);\n    Configuration configuration = loggerContext.getConfiguration();\n    configuration.addLogger(LOGGER_NAME, new LoggerConfig());\n    return configuration.getLoggerConfig(LOGGER_NAME);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/NotFoundTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.apache.http.HttpStatus;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\n\n/**\n * @author Sergii Karpenko\n */\npublic class NotFoundTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  @Test\n  public void shouldReturnEmptyMono() throws InterruptedException {\n\n    String orderUrl = \"/icecream/orders/2\";\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_NOT_FOUND)));\n\n    IcecreamServiceApi client = builder()\n        .decode404()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    client.findOrder(2).test()\n            .await()\n            .assertSubscribed()\n            .assertNoValues()\n            .assertNoErrors()\n            .assertComplete();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/ReactivityTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.awaitility.Duration;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\nimport reactivefeign.rx2.testcase.domain.IceCreamOrder;\nimport reactivefeign.rx2.testcase.domain.OrderGenerator;\n\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static org.awaitility.Awaitility.waitAtMost;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ReactivityTest {\n\n  public static final int DELAY_IN_MILLIS = 500;\n  public static final int CALLS_NUMBER = 100;\n  public static final int REACTIVE_GAIN_RATIO = 10;\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig()\n          .asynchronousResponseEnabled(true)\n          .dynamicPort());\n\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  @Test\n  public void shouldRunReactively() throws JsonProcessingException {\n\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(orderStr)\n            .withFixedDelay(DELAY_IN_MILLIS)));\n\n    IcecreamServiceApi client = builder()\n        .target(IcecreamServiceApi.class,\n            \"http://localhost:\" + wireMockRule.port());\n\n    AtomicInteger counter = new AtomicInteger();\n\n    new Thread(() -> {\n      for (int i = 0; i < CALLS_NUMBER; i++) {\n        client.findFirstOrder()\n            .doOnSuccess(order -> counter.incrementAndGet())\n            .subscribe();\n      }\n    }).start();\n\n    int timeToCompleteReactively = CALLS_NUMBER * DELAY_IN_MILLIS / REACTIVE_GAIN_RATIO;\n    waitAtMost(new Duration(timeToCompleteReactively, TimeUnit.MILLISECONDS))\n        .until(() -> counter.get() == CALLS_NUMBER);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/ReadTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.client.ReadTimeoutException;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ReadTimeoutTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options){\n    return Rx2ReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n\n  @Test\n  public void shouldFailOnReadTimeout() throws InterruptedException {\n\n    String orderUrl = \"/icecream/orders/1\";\n\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withFixedDelay(200)));\n\n    IcecreamServiceApi client = builder(\n        new ReactiveOptions.Builder()\n            .setConnectTimeoutMillis(300)\n            .setReadTimeoutMillis(100)\n            .build())\n                .target(IcecreamServiceApi.class,\n                    \"http://localhost:\" + wireMockRule.port());\n\n    client.findOrder(1).test()\n            .await()\n            .assertSubscribed()\n            .assertError(ReadTimeoutException.class);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/RequestInterceptorTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport feign.FeignException;\nimport org.apache.http.HttpStatus;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\nimport reactivefeign.rx2.testcase.domain.IceCreamOrder;\nimport reactivefeign.rx2.testcase.domain.OrderGenerator;\nimport reactivefeign.utils.Pair;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static java.util.Collections.singletonList;\nimport static reactivefeign.rx2.TestUtils.equalsComparingFieldByFieldRecursivelyRx;\n\n/**\n * @author Sergii Karpenko\n */\npublic class RequestInterceptorTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  @Test\n  public void shouldInterceptRequestAndSetAuthHeader() throws JsonProcessingException, InterruptedException {\n\n    String orderUrl = \"/icecream/orders/1\";\n\n    IceCreamOrder orderGenerated = new OrderGenerator().generate(1);\n    String orderStr = TestUtils.MAPPER.writeValueAsString(orderGenerated);\n\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_UNAUTHORIZED)))\n        .setPriority(100);\n\n    wireMockRule.stubFor(get(urlEqualTo(orderUrl))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .withHeader(\"Authorization\", equalTo(\"Bearer mytoken123\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(orderStr)))\n        .setPriority(1);\n\n    IcecreamServiceApi clientWithoutAuth = builder()\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    clientWithoutAuth.findFirstOrder().test()\n            .await()\n            .assertSubscribed()\n            .assertError(FeignException.class);\n\n    IcecreamServiceApi clientWithAuth = builder()\n        .addHeaders(singletonList(new Pair<>(\"Authorization\", \"Bearer mytoken123\")))\n        .target(IcecreamServiceApi.class,\n            \"http://localhost:\" + wireMockRule.port());\n\n    clientWithAuth.findFirstOrder().test()\n            .await()\n            .assertSubscribed()\n            .assertValue(equalsComparingFieldByFieldRecursivelyRx(orderGenerated))\n            .assertNoErrors()\n            .assertComplete();\n\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/SmokeTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport io.reactivex.Single;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\nimport reactivefeign.rx2.testcase.domain.*;\n\nimport java.util.Map;\nimport java.util.stream.Collectors;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static org.assertj.core.api.Assertions.assertThat;\nimport static reactivefeign.rx2.TestUtils.equalsComparingFieldByFieldRecursively;\nimport static reactivefeign.rx2.TestUtils.equalsComparingFieldByFieldRecursivelyRx;\n\n/**\n * @author Sergii Karpenko\n */\n\npublic class SmokeTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  @Before\n  public void resetServers() {\n    wireMockRule.resetAll();\n  }\n\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  private IcecreamServiceApi client;\n\n  private OrderGenerator generator = new OrderGenerator();\n  private Map<Integer, IceCreamOrder> orders = generator.generateRange(10).stream()\n      .collect(Collectors.toMap(IceCreamOrder::getId, o -> o));\n\n  @Rule\n  public ExpectedException expectedException = ExpectedException.none();\n\n  @Before\n  public void setUp() {\n    String targetUrl = \"http://localhost:\" + wireMockRule.port();\n    client = builder()\n        .decode404()\n        .target(IcecreamServiceApi.class, targetUrl);\n  }\n\n  @Test\n  public void testSimpleGet_success() throws JsonProcessingException, InterruptedException {\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/flavors\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(Flavor.values()))));\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/mixins\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(Mixin.values()))));\n\n    client.getAvailableFlavors().test()\n            .await()\n            .assertResult(Flavor.values());\n\n    client.getAvailableMixins().test()\n            .await()\n            .assertResult(Mixin.values());\n    }\n\n  @Test\n  public void testFindOrder_success() throws JsonProcessingException, InterruptedException {\n    IceCreamOrder orderExpected = orders.get(1);\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(orderExpected))));\n\n    client.findOrder(1).test()\n            .await()\n            .assertSubscribed()\n            .assertValue(equalsComparingFieldByFieldRecursivelyRx(orderExpected))\n            .assertNoErrors()\n            .assertComplete();\n  }\n\n  @Test\n  public void testFindOrder_empty() throws InterruptedException {\n\n    client.findOrder(123).test()\n            .await()\n            .assertSubscribed()\n            .assertNoValues()\n            .assertNoErrors()\n            .assertComplete();\n  }\n\n  @Test\n  public void testMakeOrder_success() throws JsonProcessingException {\n\n    IceCreamOrder order = new OrderGenerator().generate(20);\n    Bill billExpected = Bill.makeBill(order);\n\n    wireMockRule.stubFor(post(urlEqualTo(\"/icecream/orders\"))\n        .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(order)))\n        .willReturn(aResponse().withStatus(200)\n            .withHeader(\"Content-Type\", \"application/json\")\n            .withBody(TestUtils.MAPPER.writeValueAsString(billExpected))));\n\n    Single<Bill> bill = client.makeOrder(order);\n    assertThat(bill.blockingGet())\n            .matches(equalsComparingFieldByFieldRecursively(billExpected));\n  }\n\n  @Test\n  public void testPayBill_success() throws JsonProcessingException {\n\n    Bill bill = Bill.makeBill(new OrderGenerator().generate(30));\n\n    wireMockRule.stubFor(post(urlEqualTo(\"/icecream/bills/pay\"))\n            .withRequestBody(equalTo(TestUtils.MAPPER.writeValueAsString(bill)))\n            .willReturn(aResponse().withStatus(200)\n                    .withHeader(\"Content-Type\", \"application/json\")\n                    .withBody(Long.toString(321))));\n\n    Single<Long> result = client.payBill(bill);\n    assertThat(result.blockingGet()).isEqualTo(321);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/StatusHandlerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport com.github.tomakehurst.wiremock.junit.WireMockClassRule;\nimport feign.RetryableException;\nimport org.apache.http.HttpStatus;\nimport org.junit.Before;\nimport org.junit.ClassRule;\nimport org.junit.Test;\nimport reactivefeign.rx2.testcase.IcecreamServiceApi;\n\nimport static com.github.tomakehurst.wiremock.client.WireMock.*;\nimport static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;\nimport static reactivefeign.rx2.client.statushandler.Rx2StatusHandlers.throwOnStatus;\n\n/**\n * @author Sergii Karpenko\n */\npublic class StatusHandlerTest {\n\n  @ClassRule\n  public static WireMockClassRule wireMockRule = new WireMockClassRule(\n      wireMockConfig().dynamicPort());\n\n  protected Rx2ReactiveFeign.Builder<IcecreamServiceApi> builder(){\n    return Rx2ReactiveFeign.builder();\n  }\n\n  @Before\n  public void resetServers() {\n    wireMockRule.resetAll();\n  }\n\n  @Test\n  public void shouldThrowRetryException() throws InterruptedException {\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/1\"))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_SERVICE_UNAVAILABLE)));\n    IcecreamServiceApi client = builder()\n        .statusHandler(throwOnStatus(\n            status -> status == HttpStatus.SC_SERVICE_UNAVAILABLE,\n            (methodTag, response) -> new RetryableException(\"Should retry on next node\", null)))\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    client.findFirstOrder().test()\n            .await()\n            .assertError(RetryableException.class);\n  }\n\n  @Test\n  public void shouldThrowOnStatusCode() throws InterruptedException {\n\n    wireMockRule.stubFor(get(urlEqualTo(\"/icecream/orders/2\"))\n        .withHeader(\"Accept\", equalTo(\"application/json\"))\n        .willReturn(aResponse().withStatus(HttpStatus.SC_UNAUTHORIZED)));\n\n\n    IcecreamServiceApi client = builder()\n        .statusHandler(\n            throwOnStatus(\n                status -> status == HttpStatus.SC_UNAUTHORIZED,\n                (methodTag, response) -> new RuntimeException(\"Should login\", null)))\n        .target(IcecreamServiceApi.class, \"http://localhost:\" + wireMockRule.port());\n\n    client.findOrder(2).test()\n            .await()\n            .assertError(RuntimeException.class);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/TestUtils.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;\n\nimport java.util.function.Predicate;\n\n\n/**\n * Helper methods for tests.\n */\nclass TestUtils {\n  static final ObjectMapper MAPPER;\n\n  static {\n    MAPPER = new ObjectMapper();\n    MAPPER.registerModule(new JavaTimeModule());\n  }\n\n  public static <T> Predicate<T> equalsComparingFieldByFieldRecursively(T rhs) {\n    return lhs -> {\n      try {\n        return MAPPER.writeValueAsString(lhs).equals(MAPPER.writeValueAsString(rhs));\n      } catch (JsonProcessingException e) {\n        throw new RuntimeException(e);\n      }\n    };\n  }\n\n  public static <T> io.reactivex.functions.Predicate<T> equalsComparingFieldByFieldRecursivelyRx(T rhs) {\n    return lhs -> {\n      try {\n        return MAPPER.writeValueAsString(lhs).equals(MAPPER.writeValueAsString(rhs));\n      } catch (JsonProcessingException e) {\n        throw new RuntimeException(e);\n      }\n    };\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/IcecreamServiceApi.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.testcase;\n\nimport feign.Headers;\nimport feign.Param;\nimport feign.RequestLine;\nimport io.reactivex.Flowable;\nimport io.reactivex.Maybe;\nimport io.reactivex.Observable;\nimport io.reactivex.Single;\nimport reactivefeign.rx2.testcase.domain.Bill;\nimport reactivefeign.rx2.testcase.domain.Flavor;\nimport reactivefeign.rx2.testcase.domain.IceCreamOrder;\nimport reactivefeign.rx2.testcase.domain.Mixin;\n\n/**\n * API of an iceream web service.\n *\n * @author Sergii Karpenko\n */\n@Headers({\"Accept: application/json\"})\npublic interface IcecreamServiceApi {\n\n  RuntimeException RUNTIME_EXCEPTION = new RuntimeException(\"tests exception\");\n\n  @RequestLine(\"GET /icecream/flavors\")\n  Flowable<Flavor> getAvailableFlavors();\n\n  @RequestLine(\"GET /icecream/mixins\")\n  Observable<Mixin> getAvailableMixins();\n\n  @RequestLine(\"POST /icecream/orders\")\n  @Headers(\"Content-Type: application/json\")\n  Single<Bill> makeOrder(IceCreamOrder order);\n\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  Maybe<IceCreamOrder> findOrder(@Param(\"orderId\") int orderId);\n\n  @RequestLine(\"POST /icecream/bills/pay\")\n  @Headers(\"Content-Type: application/json\")\n  Single<Long> payBill(Bill bill);\n\n  default Maybe<IceCreamOrder> findFirstOrder() {\n    return findOrder(1);\n  }\n\n  default Single<IceCreamOrder> throwsException() {\n    throw RUNTIME_EXCEPTION;\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/IcecreamServiceApiBroken.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.testcase;\n\nimport feign.Headers;\nimport feign.Param;\nimport feign.RequestLine;\nimport io.reactivex.Flowable;\nimport io.reactivex.Single;\nimport reactivefeign.ReactiveContract;\nimport reactivefeign.rx2.testcase.domain.Bill;\nimport reactivefeign.rx2.testcase.domain.Flavor;\nimport reactivefeign.rx2.testcase.domain.IceCreamOrder;\nimport reactivefeign.rx2.testcase.domain.Mixin;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.util.Collection;\n\n/**\n * API of an iceream web service with one method that doesn't returns {@link Mono} or {@link Flux}\n * and violates {@link ReactiveContract}s rules.\n *\n * @author Sergii Karpenko\n */\npublic interface IcecreamServiceApiBroken {\n\n  @RequestLine(\"GET /icecream/flavors\")\n  Single<Collection<Flavor>> getAvailableFlavors();\n\n  @RequestLine(\"GET /icecream/mixins\")\n  Flowable<Mixin> getAvailableMixins();\n\n  /**\n   * Method that doesn't respects contract.\n   */\n  @RequestLine(\"GET /icecream/orders/{orderId}\")\n  IceCreamOrder findOrder(@Param(\"orderId\") int orderId);\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Bill.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.testcase.domain;\n\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Bill for consumed ice cream.\n */\npublic class Bill {\n  private static final Map<Integer, Float> PRICES = new HashMap<>();\n\n  static {\n    PRICES.put(1, (float) 2.00); // two euros for one ball (expensive!)\n    PRICES.put(3, (float) 2.85); // 2.85€ for 3 balls\n    PRICES.put(5, (float) 4.30); // 4.30€ for 5 balls\n    PRICES.put(7, (float) 5); // only five euros for seven balls! Wow\n  }\n\n  private static final float MIXIN_PRICE = (float) 0.6; // price per mixin\n\n  private Float price;\n\n  public Bill() {}\n\n  public Bill(final Float price) {\n    this.price = price;\n  }\n\n  public Float getPrice() {\n    return price;\n  }\n\n  public void setPrice(final Float price) {\n    this.price = price;\n  }\n\n  /**\n   * Makes a bill from an order.\n   *\n   * @param order ice cream order\n   * @return bill\n   */\n  public static Bill makeBill(final IceCreamOrder order) {\n    int nbBalls = order.getBalls().values().stream().mapToInt(Integer::intValue)\n        .sum();\n    Float price = PRICES.get(nbBalls) + order.getMixins().size() * MIXIN_PRICE;\n    return new Bill(price);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Flavor.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.testcase.domain;\n\n/**\n * Ice cream flavors.\n */\npublic enum Flavor {\n  STRAWBERRY, CHOCOLATE, BANANA, PISTACHIO, MELON, VANILLA\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/IceCreamOrder.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.testcase.domain;\n\nimport java.time.Instant;\nimport java.util.*;\n\n/**\n * Give me some ice-cream! :p\n */\npublic class IceCreamOrder {\n  private static Random random = new Random();\n\n  private int id; // order id\n  private Map<Flavor, Integer> balls; // how much balls of flavor\n  private Set<Mixin> mixins; // and some mixins ...\n  private Instant orderTimestamp; // and give it to me right now !\n\n  IceCreamOrder() {}\n\n  IceCreamOrder(int id) {\n    this(id, Instant.now());\n  }\n\n  IceCreamOrder(int id, final Instant orderTimestamp) {\n    this.id = id;\n    this.balls = new HashMap<>();\n    this.mixins = new HashSet<>();\n    this.orderTimestamp = orderTimestamp;\n  }\n\n  IceCreamOrder addBall(final Flavor ballFlavor) {\n    final Integer ballCount = balls.containsKey(ballFlavor)\n        ? balls.get(ballFlavor) + 1\n        : 1;\n    balls.put(ballFlavor, ballCount);\n    return this;\n  }\n\n  IceCreamOrder addMixin(final Mixin mixin) {\n    mixins.add(mixin);\n    return this;\n  }\n\n  IceCreamOrder withOrderTimestamp(final Instant orderTimestamp) {\n    this.orderTimestamp = orderTimestamp;\n    return this;\n  }\n\n  public int getId() {\n    return id;\n  }\n\n  public Map<Flavor, Integer> getBalls() {\n    return balls;\n  }\n\n  public Set<Mixin> getMixins() {\n    return mixins;\n  }\n\n  public Instant getOrderTimestamp() {\n    return orderTimestamp;\n  }\n\n  @Override\n  public String toString() {\n    return \"IceCreamOrder{\" + \" id=\" + id + \", balls=\" + balls + \", mixins=\" + mixins\n        + \", orderTimestamp=\" + orderTimestamp + '}';\n  }\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/Mixin.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.testcase.domain;\n\n/**\n * Ice cream mix-ins.\n */\npublic enum Mixin {\n  COOKIES, MNMS, CHOCOLATE_SIROP, STRAWBERRY_SIROP, NUTS, RAINBOW\n}\n"
  },
  {
    "path": "feign-reactor-rx2/src/test/java/reactivefeign/rx2/testcase/domain/OrderGenerator.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.rx2.testcase.domain;\n\nimport java.time.Instant;\nimport java.time.temporal.ChronoUnit;\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Random;\nimport java.util.stream.Collectors;\nimport java.util.stream.IntStream;\n\n/**\n * Generator of random ice cream orders.\n */\npublic class OrderGenerator {\n  private static final int[] BALLS_NUMBER = {1, 3, 5, 7};\n  private static final int[] MIXIN_NUMBER = {1, 2, 3};\n\n  private static final Random random = new Random();\n\n  public IceCreamOrder generate(int id) {\n    final IceCreamOrder order = new IceCreamOrder(id);\n    final int nbBalls = peekBallsNumber();\n    final int nbMixins = peekMixinNumber();\n\n    IntStream.rangeClosed(1, nbBalls).mapToObj(i -> this.peekFlavor())\n        .forEach(order::addBall);\n\n    IntStream.rangeClosed(1, nbMixins).mapToObj(i -> this.peekMixin())\n        .forEach(order::addMixin);\n\n    return order;\n  }\n\n  public Collection<IceCreamOrder> generateRange(int n) {\n    Instant now = Instant.now();\n\n    List<Instant> orderTimestamps = IntStream.range(0, n)\n        .mapToObj(minutes -> now.minus(minutes, ChronoUnit.MINUTES))\n        .collect(Collectors.toList());\n\n    return IntStream.range(0, n)\n        .mapToObj(\n            i -> this.generate(i).withOrderTimestamp(orderTimestamps.get(i)))\n        .collect(Collectors.toList());\n  }\n\n  private int peekBallsNumber() {\n    return BALLS_NUMBER[random.nextInt(BALLS_NUMBER.length)];\n  }\n\n  private int peekMixinNumber() {\n    return MIXIN_NUMBER[random.nextInt(MIXIN_NUMBER.length)];\n  }\n\n  private Flavor peekFlavor() {\n    return Flavor.values()[random.nextInt(Flavor.values().length)];\n  }\n\n  private Mixin peekMixin() {\n    return Mixin.values()[random.nextInt(Mixin.values().length)];\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.github.reactivefeign</groupId>\n        <artifactId>feign-reactor</artifactId>\n        <version>1.0.0-SNAPSHOT</version>\n    </parent>\n\n    <artifactId>feign-reactor-webclient</artifactId>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.github.reactivefeign</groupId>\n            <artifactId>feign-reactor-core</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-webflux</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.springframework</groupId>\n            <artifactId>spring-web</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.projectreactor.ipc</groupId>\n            <artifactId>reactor-netty</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>io.netty</groupId>\n            <artifactId>netty-all</artifactId>\n        </dependency>\n\n        <!-- Tests -->\n        <dependency>\n            <groupId>io.github.reactivefeign</groupId>\n            <artifactId>feign-reactor-core</artifactId>\n            <version>1.0.0-SNAPSHOT</version>\n            <type>test-jar</type>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-webflux</artifactId>\n            <exclusions>\n                <exclusion>\n                    <artifactId>spring-boot-starter-logging</artifactId>\n                    <groupId>org.springframework.boot</groupId>\n                </exclusion>\n            </exclusions>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.springframework.boot</groupId>\n            <artifactId>spring-boot-starter-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.projectreactor</groupId>\n            <artifactId>reactor-test</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>com.github.tomakehurst</groupId>\n            <artifactId>wiremock</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.apache.logging.log4j</groupId>\n            <artifactId>log4j-slf4j-impl</artifactId>\n            <scope>test</scope>\n        </dependency>\n\n        <dependency>\n            <groupId>org.awaitility</groupId>\n            <artifactId>awaitility</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "feign-reactor-webclient/src/main/java/reactivefeign/webclient/WebReactiveFeign.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport io.netty.channel.ChannelOption;\nimport io.netty.handler.timeout.ReadTimeoutHandler;\nimport org.springframework.http.client.reactive.ReactorClientHttpConnector;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\n\nimport java.util.concurrent.TimeUnit;\n\nimport static reactivefeign.webclient.client.WebReactiveHttpClient.webClient;\n\n/**\n * {@link WebClient} based implementation of reactive Feign\n *\n * @author Sergii Karpenko\n */\npublic class WebReactiveFeign {\n\n    public static final int DEFAULT_READ_TIMEOUT_MILLIS = 10000;\n    public static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 5000;\n\n    public static <T> Builder<T> builder() {\n    return new Builder<>();\n  }\n\n  public static <T> Builder<T> builder(WebClient webClient) {\n        return new Builder<>(webClient);\n    }\n\n  public static class Builder<T> extends ReactiveFeign.Builder<T> {\n\n      protected WebClient webClient;\n\n      protected Builder() {\n          this(WebClient.create());\n      }\n\n      protected Builder(WebClient webClient) {\n          setWebClient(webClient);\n          options(new ReactiveOptions.Builder()\n                  .setConnectTimeoutMillis(DEFAULT_CONNECT_TIMEOUT_MILLIS)\n                  .setReadTimeoutMillis(DEFAULT_READ_TIMEOUT_MILLIS)\n                  .build());\n      }\n\n      @Override\n      public Builder<T> options(ReactiveOptions options) {\n          if (!options.isEmpty()) {\n              ReactorClientHttpConnector connector = new ReactorClientHttpConnector(\n                      opts -> {\n                          if (options.getConnectTimeoutMillis() != null) {\n                              opts.option(ChannelOption.CONNECT_TIMEOUT_MILLIS,\n                                      options.getConnectTimeoutMillis().intValue());\n                          }\n                          if (options.getReadTimeoutMillis() != null) {\n                              opts.afterNettyContextInit(ctx -> {\n                                  ctx.addHandlerLast(new ReadTimeoutHandler(\n                                          options.getReadTimeoutMillis(),\n                                          TimeUnit.MILLISECONDS));\n\n                              });\n                          }\n                          if (options.isTryUseCompression() != null) {\n                              opts.compression(options.isTryUseCompression());\n                          }\n                      });\n\n              setWebClient(webClient.mutate().clientConnector(connector).build());\n          }\n          return this;\n      }\n\n      protected void setWebClient(WebClient webClient){\n          this.webClient = webClient;\n          clientFactory(methodMetadata -> webClient(methodMetadata, webClient));\n      }\n  }\n}\n\n\n"
  },
  {
    "path": "feign-reactor-webclient/src/main/java/reactivefeign/webclient/client/WebReactiveHttpClient.java",
    "content": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reactivefeign.webclient.client;\n\nimport feign.MethodMetadata;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.http.HttpHeaders;\nimport org.springframework.http.HttpMethod;\nimport org.springframework.http.client.reactive.ClientHttpRequest;\nimport org.springframework.web.reactive.function.BodyInserter;\nimport org.springframework.web.reactive.function.BodyInserters;\nimport org.springframework.web.reactive.function.client.WebClient;\nimport reactivefeign.client.ReactiveHttpClient;\nimport reactivefeign.client.ReactiveHttpRequest;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactivefeign.client.ReadTimeoutException;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.ParameterizedType;\nimport java.lang.reflect.Type;\n\nimport static feign.Util.resolveLastTypeParameter;\nimport static java.util.Optional.ofNullable;\nimport static reactivefeign.utils.FeignUtils.getBodyActualType;\n\n/**\n * Uses {@link WebClient} to execute http requests\n * @author Sergii Karpenko\n */\npublic class WebReactiveHttpClient implements ReactiveHttpClient {\n\n\tprivate final WebClient webClient;\n\tprivate final ParameterizedTypeReference<Object> bodyActualType;\n\tprivate final Type returnPublisherType;\n\tprivate final ParameterizedTypeReference<Object> returnActualType;\n\n\tpublic static WebReactiveHttpClient webClient(MethodMetadata methodMetadata, WebClient webClient) {\n\n\t\tfinal Type returnType = methodMetadata.returnType();\n\t\tType returnPublisherType = ((ParameterizedType) returnType).getRawType();\n\t\tParameterizedTypeReference<Object> returnActualType = ParameterizedTypeReference.forType(\n\t\t\t\tresolveLastTypeParameter(returnType, (Class<?>) returnPublisherType));\n\n\t\tParameterizedTypeReference<Object> bodyActualType = ofNullable(\n\t\t\t\tgetBodyActualType(methodMetadata.bodyType()))\n\t\t\t\t.map(type -> ParameterizedTypeReference.forType(type))\n\t\t\t\t.orElse(null);\n\n\t\treturn new WebReactiveHttpClient(webClient,\n\t\t\t\tbodyActualType, returnPublisherType, returnActualType);\n\t}\n\n\tpublic WebReactiveHttpClient(WebClient webClient,\n\t\t\t\t\t\t\t\t ParameterizedTypeReference<Object> bodyActualType,\n\t\t\t\t\t\t\t\t Type returnPublisherType, ParameterizedTypeReference<Object> returnActualType) {\n\t\tthis.webClient = webClient;\n\t\tthis.bodyActualType = bodyActualType;\n\t\tthis.returnPublisherType = returnPublisherType;\n\t\tthis.returnActualType = returnActualType;\n\t}\n\n\t@Override\n\tpublic Mono<ReactiveHttpResponse> executeRequest(ReactiveHttpRequest request) {\n\t\treturn webClient.method(HttpMethod.valueOf(request.method()))\n\t\t\t\t.uri(request.uri())\n\t\t\t\t.headers(httpHeaders -> setUpHeaders(request, httpHeaders))\n\t\t\t\t.body(provideBody(request))\n\t\t\t\t.exchange()\n\t\t\t\t.onErrorMap(ex -> ex instanceof io.netty.handler.timeout.ReadTimeoutException,\n\t\t\t\t\t\tReadTimeoutException::new)\n\t\t\t\t.map(response -> new WebReactiveHttpResponse(response, returnPublisherType, returnActualType));\n\t}\n\n\tprotected BodyInserter<?, ? super ClientHttpRequest> provideBody(ReactiveHttpRequest request) {\n\t\treturn bodyActualType != null\n                ? BodyInserters.fromPublisher(request.body(), bodyActualType)\n                : BodyInserters.empty();\n\t}\n\n\tprotected void setUpHeaders(ReactiveHttpRequest request, HttpHeaders httpHeaders) {\n\t\trequest.headers().forEach(httpHeaders::put);\n\t}\n\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/main/java/reactivefeign/webclient/client/WebReactiveHttpResponse.java",
    "content": "package reactivefeign.webclient.client;\n\nimport org.reactivestreams.Publisher;\nimport org.springframework.core.ParameterizedTypeReference;\nimport org.springframework.core.io.ByteArrayResource;\nimport org.springframework.web.reactive.function.client.ClientResponse;\nimport reactivefeign.client.ReactiveHttpResponse;\nimport reactor.core.publisher.Flux;\nimport reactor.core.publisher.Mono;\n\nimport java.lang.reflect.Type;\nimport java.util.List;\nimport java.util.Map;\n\nclass WebReactiveHttpResponse implements ReactiveHttpResponse{\n\n\tprivate final ClientResponse clientResponse;\n\tprivate final Type returnPublisherType;\n\tprivate final ParameterizedTypeReference<Object> returnActualType;\n\n\tWebReactiveHttpResponse(ClientResponse clientResponse,\n\t\t\t\t\t\t\t\t   Type returnPublisherType, ParameterizedTypeReference<Object> returnActualType) {\n\t\tthis.clientResponse = clientResponse;\n\t\tthis.returnPublisherType = returnPublisherType;\n\t\tthis.returnActualType = returnActualType;\n\t}\n\n\t@Override\n\tpublic int status() {\n\t\treturn clientResponse.statusCode().value();\n\t}\n\n\t@Override\n\tpublic Map<String, List<String>> headers() {\n\t\treturn clientResponse.headers().asHttpHeaders();\n\t}\n\n\t@Override\n\tpublic Publisher<Object> body() {\n\t\tif (returnPublisherType == Mono.class) {\n\t\t\treturn clientResponse.bodyToMono(returnActualType);\n\t\t} else if(returnPublisherType == Flux.class){\n\t\t\treturn clientResponse.bodyToFlux(returnActualType);\n\t\t} else {\n\t\t\tthrow new IllegalArgumentException(\"Unknown returnPublisherType: \" + returnPublisherType);\n\t\t}\n\t}\n\n\t@Override\n\tpublic Mono<byte[]> bodyData() {\n\t\treturn clientResponse.bodyToMono(ByteArrayResource.class)\n\t\t\t\t.map(ByteArrayResource::getByteArray)\n\t\t\t\t.defaultIfEmpty(new byte[0]);\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/CompressionTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class CompressionTest extends reactivefeign.CompressionTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return WebReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/ConnectionTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ConnectionTimeoutTest extends reactivefeign.ConnectionTimeoutTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return WebReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/ContractTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ContractTest extends reactivefeign.ContractTest {\n\n  @Override\n  protected <T> ReactiveFeign.Builder<T> builder() {\n    return WebReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/DefaultMethodTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class DefaultMethodTest extends reactivefeign.DefaultMethodTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return WebReactiveFeign.builder();\n  }\n\n  @Override\n  protected <API> ReactiveFeign.Builder<API> builder(Class<API> apiClass) {\n    return WebReactiveFeign.builder();\n  }\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return WebReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/LoggerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class LoggerTest extends reactivefeign.LoggerTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return WebReactiveFeign.builder();\n  }\n\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/NotFoundTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class NotFoundTest extends reactivefeign.NotFoundTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return WebReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/ReactivityTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport com.fasterxml.jackson.core.JsonProcessingException;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\npublic class ReactivityTest extends reactivefeign.ReactivityTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return WebReactiveFeign.builder();\n  }\n\n  @Override\n  public void shouldRunReactively() throws JsonProcessingException {\n    super.shouldRunReactively();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/ReadTimeoutTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.ReactiveOptions;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class ReadTimeoutTest extends reactivefeign.ReadTimeoutTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder(ReactiveOptions options) {\n    return WebReactiveFeign.<IcecreamServiceApi>builder().options(options);\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/RequestInterceptorTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class RequestInterceptorTest extends reactivefeign.RequestInterceptorTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return WebReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/RetryingTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class RetryingTest extends reactivefeign.RetryingTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return WebReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/SmokeTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class SmokeTest extends reactivefeign.SmokeTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return WebReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/StatusHandlerTest.java",
    "content": "/**\n * Copyright 2018 The Feign Authors\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\"); you may not use this file except\n * in compliance with the License. You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software distributed under the License\n * is distributed on an \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express\n * or implied. See the License for the specific language governing permissions and limitations under\n * the License.\n */\npackage reactivefeign.webclient;\n\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.testcase.IcecreamServiceApi;\n\n/**\n * @author Sergii Karpenko\n */\npublic class StatusHandlerTest extends reactivefeign.StatusHandlerTest {\n\n  @Override\n  protected ReactiveFeign.Builder<IcecreamServiceApi> builder() {\n    return WebReactiveFeign.builder();\n  }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/AllFeaturesTest.java",
    "content": "/*\n * Copyright 2013-2015 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage reactivefeign.webclient.allfeatures;\n\nimport org.junit.Ignore;\nimport org.junit.Test;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration;\nimport org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport reactivefeign.ReactiveFeign;\nimport reactivefeign.allfeatures.AllFeaturesController;\nimport reactivefeign.webclient.WebReactiveFeign;\n\n/**\n * @author Sergii Karpenko\n *\n * Tests ReactiveFeign in conjunction with WebFlux rest controller.\n */\n@EnableAutoConfiguration(exclude = {ReactiveSecurityAutoConfiguration.class, ReactiveUserDetailsServiceAutoConfiguration.class})\npublic class AllFeaturesTest extends reactivefeign.allfeatures.AllFeaturesTest {\n\n\t@Override\n\tprotected ReactiveFeign.Builder<reactivefeign.allfeatures.AllFeaturesApi> builder() {\n\t\treturn WebReactiveFeign.builder();\n\t}\n\n    //Netty's WebClient is not able to do this trick\n\t@Ignore\n\t@Test\n\t@Override\n\tpublic void shouldReturnFirstResultBeforeSecondSent() {\n\t}\n\n\t//WebClient is not able to do this\n\t@Ignore\n\t@Test\n\t@Override\n\tpublic void shouldMirrorStringStreamBody() {\n\t}\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesApi.java",
    "content": "package reactivefeign.webclient.allfeatures;\n\nimport feign.Headers;\nimport feign.RequestLine;\nimport org.reactivestreams.Publisher;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport reactor.core.publisher.Flux;\n\nimport java.nio.ByteBuffer;\n\nimport static org.springframework.http.MediaType.APPLICATION_OCTET_STREAM_VALUE;\n\npublic interface WebClientFeaturesApi {\n\n    @RequestLine(\"POST \" + \"/mirrorStreamingBinaryBodyReactive\")\n    @Headers({ \"Content-Type: \"+APPLICATION_OCTET_STREAM_VALUE })\n    Flux<DataBuffer> mirrorStreamingBinaryBodyReactive(Publisher<DataBuffer> body);\n\n    @RequestLine(\"POST \" + \"/mirrorResourceReactiveWithZeroCopying\")\n    @Headers({ \"Content-Type: \"+APPLICATION_OCTET_STREAM_VALUE })\n    Flux<DataBuffer> mirrorResourceReactiveWithZeroCopying(Resource resource);\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesController.java",
    "content": "package reactivefeign.webclient.allfeatures;\n\nimport org.reactivestreams.Publisher;\nimport org.springframework.core.io.Resource;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferUtils;\nimport org.springframework.core.io.buffer.DefaultDataBufferFactory;\nimport org.springframework.web.bind.annotation.PostMapping;\nimport org.springframework.web.bind.annotation.RequestBody;\nimport org.springframework.web.bind.annotation.RestController;\nimport reactor.core.publisher.Flux;\n\n@RestController\npublic class WebClientFeaturesController implements WebClientFeaturesApi{\n\n    @PostMapping(path = \"/mirrorStreamingBinaryBodyReactive\")\n    @Override\n    public Flux<DataBuffer> mirrorStreamingBinaryBodyReactive(@RequestBody Publisher<DataBuffer> body) {\n        return Flux.from(body);\n    }\n\n    @PostMapping(path = \"/mirrorResourceReactiveWithZeroCopying\")\n    @Override\n    public Flux<DataBuffer> mirrorResourceReactiveWithZeroCopying(@RequestBody Resource resource) {\n        return DataBufferUtils.read(resource, new DefaultDataBufferFactory(), 3);\n    }\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/java/reactivefeign/webclient/allfeatures/WebClientFeaturesTest.java",
    "content": "package reactivefeign.webclient.allfeatures;\n\n\nimport org.junit.Before;\nimport org.junit.Rule;\nimport org.junit.Test;\nimport org.junit.rules.ExpectedException;\nimport org.junit.runner.RunWith;\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\nimport org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration;\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.boot.web.server.LocalServerPort;\nimport org.springframework.core.io.ByteArrayResource;\nimport org.springframework.core.io.buffer.DataBuffer;\nimport org.springframework.core.io.buffer.DataBufferUtils;\nimport org.springframework.core.io.buffer.DefaultDataBufferFactory;\nimport org.springframework.test.context.junit4.SpringRunner;\nimport reactivefeign.webclient.WebReactiveFeign;\nimport reactor.core.publisher.Flux;\nimport reactor.test.StepVerifier;\n\nimport static java.nio.ByteBuffer.wrap;\nimport static org.assertj.core.api.Assertions.assertThat;\n\n@RunWith(SpringRunner.class)\n@SpringBootTest(\n        properties = {\"spring.main.web-application-type=reactive\"},\n        classes = {WebClientFeaturesController.class },\n        webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)\n@EnableAutoConfiguration(exclude = {org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration.class, ReactiveUserDetailsServiceAutoConfiguration.class})\npublic class WebClientFeaturesTest {\n\n    private WebClientFeaturesApi client;\n\n    @LocalServerPort\n    private int port;\n\n    @Rule\n    public ExpectedException expectedException = ExpectedException.none();\n\n    @Before\n    public void setUp() {\n        client = WebReactiveFeign.<WebClientFeaturesApi>builder()\n                .decode404()\n                .target(WebClientFeaturesApi.class, \"http://localhost:\" + port);\n    }\n\n    @Test\n    public void shouldMirrorStreamingBinaryBodyReactive()  {\n\n        Flux<DataBuffer> returned = client\n                .mirrorStreamingBinaryBodyReactive(Flux.just(\n                        fromByteArray(new byte[]{1,2,3}),\n                        fromByteArray(new byte[]{4,5,6})));\n\n        StepVerifier.create(returned)\n                .expectNextMatches(dataBuffer -> dataBuffer.asByteBuffer().equals(wrap(new byte[]{1,2,3})))\n                .expectNextMatches(dataBuffer -> dataBuffer.asByteBuffer().equals(wrap(new byte[]{4,5,6})))\n                .verifyComplete();\n    }\n\n    private static DataBuffer fromByteArray(byte[] data){\n        return new DefaultDataBufferFactory().wrap(data);\n    }\n\n    @Test\n    public void shouldMirrorResourceReactiveWithZeroCopying(){\n        byte[] data = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};\n        ByteArrayResource resource = new ByteArrayResource(data);\n        Flux<DataBuffer> returned = client.mirrorResourceReactiveWithZeroCopying(resource);\n        assertThat(DataBufferUtils.join(returned).block().asByteBuffer()).isEqualTo(wrap(data));\n    }\n\n\n}\n"
  },
  {
    "path": "feign-reactor-webclient/src/test/resources/log4j2.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Configuration status=\"WARN\">\n    <Appenders>\n        <Console name=\"Console\" target=\"SYSTEM_OUT\">\n            <PatternLayout pattern=\"%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n\"/>\n        </Console>\n    </Appenders>\n    <Loggers>\n        <Root level=\"info\">\n            <AppenderRef ref=\"Console\"/>\n        </Root>\n    </Loggers>\n</Configuration>"
  },
  {
    "path": "pom.xml",
    "content": "<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0             http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>io.github.reactivefeign</groupId>\n    <artifactId>feign-reactor</artifactId>\n    <version>1.0.0-SNAPSHOT</version>\n    <packaging>pom</packaging>\n\n    <modules>\n        <module>feign-reactor-core</module>\n        <module>feign-reactor-webclient</module>\n        <module>feign-reactor-cloud</module>\n        <module>feign-reactor-rx2</module>\n        <module>feign-reactor-jetty</module>\n    </modules>\n\n    <name>feign-reactive</name>\n    <description>Use Feign client on WebClient</description>\n    <url>https://github.com/kptfh/feign-reactive</url>\n\n    <organization>\n        <name>ReactiveFeign</name>\n        <url>https://github.com/kptfh/feign-reactive</url>\n    </organization>\n\n    <issueManagement>\n        <system>Github</system>\n        <url>https://github.com/kptfh/feign-reactive/issues</url>\n    </issueManagement>\n\n    <!--<scm>-->\n        <!--<url>https://github.com/kptfh/feign-reactive</url>-->\n        <!--<connection>scm:git:https://github.com/kptfh/feign-reactive.git</connection>-->\n        <!--<developerConnection>scm:git:https://github.com/kptfh/feign-reactive.git</developerConnection>-->\n        <!--<tag>feign-reactive-0.4.0</tag>-->\n    <!--</scm>-->\n\n    <licenses>\n        <license>\n            <name>The Apache Software License, Version 2.0</name>\n            <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n\n    <developers>\n        <developer>\n            <id>kptfh</id>\n            <name>Sergii Karpenko</name>\n            <email>sergey.karpenko@gmail.com</email>\n        </developer>\n    </developers>\n\n    <properties>\n        <java.version>1.8</java.version>\n        <maven.compiler.source>${java.version}</maven.compiler.source>\n        <maven.compiler.target>${java.version}</maven.compiler.target>\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>\n\n        <reactor.version>3.1.8.RELEASE</reactor.version>\n        <feign.version>9.5.1</feign.version>\n        <slf4j.version>1.7.25</slf4j.version>\n        <commons-httpclient.version>3.1</commons-httpclient.version>\n\n        <!--WebClient-->\n        <spring-webflux.version>5.0.9.RELEASE</spring-webflux.version>\n        <spring-web.version>5.0.9.RELEASE</spring-web.version>\n        <reactor-netty.version>0.7.9.RELEASE</reactor-netty.version>\n        <netty-all.version>4.1.29.Final</netty-all.version>\n        <jackson.version>2.9.7</jackson.version>\n\n        <!--Cloud-->\n        <ribbon-version>2.1.1</ribbon-version>\n        <hystrix.version>1.4.26</hystrix.version>\n        <archaius.version>0.6.6</archaius.version>\n        <rxjava.version>1.3.8</rxjava.version>\n        <rxjava-reactive-streams.version>1.2.1</rxjava-reactive-streams.version>\n\n        <!--Rx2-->\n        <reactor-adapter.version>3.1.6.RELEASE</reactor-adapter.version>\n        <rxjava2.version>2.2.2</rxjava2.version>\n\n        <!--Jetty-->\n        <jetty-reactive-httpclient.version>1.0.1</jetty-reactive-httpclient.version>\n        <json-reactor.version>0.0.1</json-reactor.version>\n\n        <!-- Tests -->\n        <spring-boot-starter-webflux.version>2.0.5.RELEASE</spring-boot-starter-webflux.version>\n        <spring-boot-starter.version>2.0.5.RELEASE</spring-boot-starter.version>\n        <junit.version>4.12</junit.version>\n        <assertj.version>3.9.0</assertj.version>\n        <wiremock.version>2.19.0</wiremock.version>\n        <jetty.version>9.4.12.v20180830</jetty.version>\n        <slf4j-log4j12.version>1.8.0-beta0</slf4j-log4j12.version>\n        <hamcrest.version>1.3</hamcrest.version>\n        <awaitility.version>3.0.0</awaitility.version>\n        <mockito.version>1.9.5</mockito.version>\n        <log4j.version>2.11.1</log4j.version>\n        <guava.version>20.0</guava.version>\n\n        <!-- Plugins -->\n        <jacoco-plugin.version>0.7.7.201606060606</jacoco-plugin.version>\n        <coveralls-plugin.version>4.1.0</coveralls-plugin.version>\n\n        <maven-compiler-plugin.version>3.5.1</maven-compiler-plugin.version>\n        <maven-surefire-plugin.version>2.19.1</maven-surefire-plugin.version>\n        <versions-maven-plugin.version>2.3</versions-maven-plugin.version>\n\n    </properties>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>io.github.reactivefeign</groupId>\n                <artifactId>feign-reactor-core</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.github.reactivefeign</groupId>\n                <artifactId>feign-reactor-webclient</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.github.reactivefeign</groupId>\n                <artifactId>feign-reactor-cloud</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.github.openfeign</groupId>\n                <artifactId>feign-core</artifactId>\n                <version>${feign.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.projectreactor</groupId>\n                <artifactId>reactor-core</artifactId>\n                <version>${reactor.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.projectreactor</groupId>\n                <artifactId>reactor-test</artifactId>\n                <version>${reactor.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>commons-httpclient</groupId>\n                <artifactId>commons-httpclient</artifactId>\n                <version>${commons-httpclient.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.slf4j</groupId>\n                <artifactId>slf4j-api</artifactId>\n                <version>${slf4j.version}</version>\n            </dependency>\n\n            <!--WebClient part-->\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-webflux</artifactId>\n                <version>${spring-webflux.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.springframework</groupId>\n                <artifactId>spring-web</artifactId>\n                <version>${spring-web.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.projectreactor.ipc</groupId>\n                <artifactId>reactor-netty</artifactId>\n                <version>${reactor-netty.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.netty</groupId>\n                <artifactId>netty-all</artifactId>\n                <version>${netty-all.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.fasterxml.jackson</groupId>\n                <artifactId>jackson-bom</artifactId>\n                <version>${jackson.version}</version>\n                <scope>import</scope>\n                <type>pom</type>\n            </dependency>\n\n            <!--Cloud part-->\n            <dependency>\n                <groupId>com.netflix.hystrix</groupId>\n                <artifactId>hystrix-core</artifactId>\n                <version>${hystrix.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.netflix.archaius</groupId>\n                <artifactId>archaius-core</artifactId>\n                <version>${archaius.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>com.netflix.ribbon</groupId>\n                <artifactId>ribbon-core</artifactId>\n                <version>${ribbon-version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.reactivex</groupId>\n                <artifactId>rxjava</artifactId>\n                <version>${rxjava.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.reactivex</groupId>\n                <artifactId>rxjava-reactive-streams</artifactId>\n                <version>${rxjava-reactive-streams.version}</version>\n            </dependency>\n\n            <!--Rx2 part-->\n            <dependency>\n                <groupId>com.netflix.ribbon</groupId>\n                <artifactId>ribbon-loadbalancer</artifactId>\n                <version>${ribbon-version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.projectreactor.addons</groupId>\n                <artifactId>reactor-adapter</artifactId>\n                <version>${reactor-adapter.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.reactivex.rxjava2</groupId>\n                <artifactId>rxjava</artifactId>\n                <version>${rxjava2.version}</version>\n            </dependency>\n\n            <!--Jetty part-->\n            <dependency>\n                <groupId>org.eclipse.jetty</groupId>\n                <artifactId>jetty-reactive-httpclient</artifactId>\n                <version>${jetty-reactive-httpclient.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>io.kptfh.reactivejson</groupId>\n                <artifactId>json-reactor</artifactId>\n                <version>${json-reactor.version}</version>\n            </dependency>\n\n            <!--Test-->\n            <dependency>\n                <groupId>junit</groupId>\n                <artifactId>junit</artifactId>\n                <version>${junit.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-webflux</artifactId>\n                <exclusions>\n                    <exclusion>\n                        <artifactId>spring-boot-starter-logging</artifactId>\n                        <groupId>org.springframework.boot</groupId>\n                    </exclusion>\n                </exclusions>\n                <version>${spring-boot-starter-webflux.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.springframework.boot</groupId>\n                <artifactId>spring-boot-starter-test</artifactId>\n                <version>${spring-boot-starter.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.hamcrest</groupId>\n                <artifactId>hamcrest-library</artifactId>\n                <version>${hamcrest.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.awaitility</groupId>\n                <artifactId>awaitility</artifactId>\n                <version>${awaitility.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.mockito</groupId>\n                <artifactId>mockito-all</artifactId>\n                <version>${mockito.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.assertj</groupId>\n                <artifactId>assertj-core</artifactId>\n                <version>${assertj.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>com.github.tomakehurst</groupId>\n                <artifactId>wiremock</artifactId>\n                <version>${wiremock.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.eclipse.jetty</groupId>\n                <artifactId>jetty-bom</artifactId>\n                <version>${jetty.version}</version>\n                <scope>import</scope>\n                <type>pom</type>\n            </dependency>\n\n            <dependency>\n                <groupId>com.fasterxml.jackson.datatype</groupId>\n                <artifactId>jackson-datatype-jsr310</artifactId>\n                <version>${jackson.version}</version>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.logging.log4j</groupId>\n                <artifactId>log4j-slf4j-impl</artifactId>\n                <version>${log4j.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>org.apache.logging.log4j</groupId>\n                <artifactId>log4j-api</artifactId>\n                <version>${log4j.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n            <dependency>\n                <groupId>com.google.guava</groupId>\n                <artifactId>guava</artifactId>\n                <version>${guava.version}</version>\n                <scope>test</scope>\n            </dependency>\n\n        </dependencies>\n    </dependencyManagement>\n\n    <repositories>\n        <repository>\n            <id>bintray-kptfh-feign-reactive</id>\n            <name>bintray</name>\n            <url>https://dl.bintray.com/kptfh/json-reactive</url>\n        </repository>\n    </repositories>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <groupId>org.springframework.boot</groupId>\n                    <artifactId>spring-boot-maven-plugin</artifactId>\n                </plugin>\n\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-compiler-plugin</artifactId>\n                    <version>${maven-compiler-plugin.version}</version>\n                    <configuration>\n                        <source>${java.version}</source>\n                        <target>${java.version}</target>\n                    </configuration>\n                </plugin>\n\n                <!-- Run tests -->\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-surefire-plugin</artifactId>\n                    <version>${maven-surefire-plugin.version}</version>\n                </plugin>\n\n                <!-- Produce code coverage -->\n                <plugin>\n                    <groupId>org.jacoco</groupId>\n                    <artifactId>jacoco-maven-plugin</artifactId>\n                    <version>${jacoco-plugin.version}</version>\n                </plugin>\n\n                <!-- Attach sources to release -->\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-source-plugin</artifactId>\n                    <executions>\n                        <execution>\n                            <id>attach-sources</id>\n                            <goals>\n                                <goal>jar</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                </plugin>\n\n                <!-- Submits coverage reports to Coveralls -->\n                <plugin>\n                    <groupId>org.eluder.coveralls</groupId>\n                    <artifactId>coveralls-maven-plugin</artifactId>\n                    <version>${coveralls-plugin.version}</version>\n                </plugin>\n\n                <!-- Attach Javadoc to release -->\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-javadoc-plugin</artifactId>\n                    <executions>\n                        <execution>\n                            <id>attach-javadocs</id>\n                            <goals>\n                                <goal>jar</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n\n        <plugins>\n            <!-- Jacoco Code Coverage -->\n            <!--<plugin>-->\n                <!--<groupId>org.jacoco</groupId>-->\n                <!--<artifactId>jacoco-maven-plugin</artifactId>-->\n                <!--<executions>-->\n                    <!--<execution>-->\n                        <!--<id>prepare-agent</id>-->\n                        <!--<goals>-->\n                            <!--<goal>prepare-agent</goal>-->\n                        <!--</goals>-->\n                    <!--</execution>-->\n                    <!--<execution>-->\n                        <!--<id>report</id>-->\n                        <!--<phase>prepare-package</phase>-->\n                        <!--<goals>-->\n                            <!--<goal>report</goal>-->\n                        <!--</goals>-->\n                    <!--</execution>-->\n                    <!--<execution>-->\n                        <!--<id>post-unit-test</id>-->\n                        <!--<phase>test</phase>-->\n                        <!--<goals>-->\n                            <!--<goal>report</goal>-->\n                        <!--</goals>-->\n                        <!--<configuration>-->\n                            <!--&lt;!&ndash; Sets the path to the file which contains the execution data. &ndash;&gt;-->\n\n                            <!--<dataFile>target/jacoco.exec</dataFile>-->\n                            <!--&lt;!&ndash; Sets the output directory for the code coverage report. &ndash;&gt;-->\n                            <!--<outputDirectory>target/jacoco-ut</outputDirectory>-->\n                        <!--</configuration>-->\n                    <!--</execution>-->\n                <!--</executions>-->\n            <!--</plugin>-->\n            <plugin>\n                <artifactId>maven-release-plugin</artifactId>\n                <version>2.4.1</version>\n                <configuration>\n                    <useReleaseProfile>false</useReleaseProfile>\n                    <releaseProfiles>release</releaseProfiles>\n                    <autoVersionSubmodules>true</autoVersionSubmodules>\n                </configuration>\n            </plugin>\n        </plugins>\n    </build>\n\n    <!-- Push binary to JCenter -->\n    <distributionManagement>\n        <repository>\n            <id>bintray-kptfh-feign-reactive</id>\n            <name>kptfh-feign-reactive</name>\n            <url>https://api.bintray.com/maven/kptfh/feign-reactive/client/;publish=1</url>\n        </repository>\n    </distributionManagement>\n\n    <reporting>\n        <plugins>\n            <plugin>\n                <groupId>org.codehaus.mojo</groupId>\n                <artifactId>versions-maven-plugin</artifactId>\n                <version>${versions-maven-plugin.version}</version>\n                <reportSets>\n                    <reportSet>\n                        <reports>\n                            <report>dependency-updates-report</report>\n                            <report>plugin-updates-report</report>\n                            <report>property-updates-report</report>\n                        </reports>\n                    </reportSet>\n                </reportSets>\n            </plugin>\n        </plugins>\n    </reporting>\n\n</project>\n"
  },
  {
    "path": "settings.xml",
    "content": "<settings xmlns=\"http://maven.apache.org/SETTINGS/1.0.0\"\n          xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\"\n          xsi:schemaLocation=\"http://maven.apache.org/SETTINGS/1.0.0\n                          https://maven.apache.org/xsd/settings-1.0.0.xsd\">\n    <servers>\n        <server>\n            <id>bintray-kptfh-feign-reactive</id>\n            <username>kptfh</username>\n            <password>4b4f2162048b2d8f2950ebd29fd604232bf5b2e4</password>\n        </server>\n    </servers>\n</settings>"
  }
]