├── core └── src │ ├── test │ ├── resources │ │ ├── mockito-extensions │ │ │ └── org.mockito.plugins.MockMaker │ │ └── template │ │ │ ├── feign-tests.json │ │ │ └── negative-tests.json │ └── java │ │ └── feign │ │ ├── template │ │ ├── UriUtilsTest.java │ │ ├── expander │ │ │ ├── DefaultExpanderTest.java │ │ │ └── ExpanderUtilsTest.java │ │ ├── ExpressionsTest.java │ │ ├── tck │ │ │ ├── TestExamples.java │ │ │ ├── TestCase.java │ │ │ └── TestGroup.java │ │ └── UriTemplateTest.java │ │ ├── exception │ │ ├── FeignExceptionTest.java │ │ └── RethrowExceptionHandlerTest.java │ │ ├── retry │ │ ├── NoRetryTest.java │ │ ├── ExceptionConditionTest.java │ │ ├── StatusCodeConditionTest.java │ │ ├── BackoffRetryIntervalTest.java │ │ └── RetryConditionTest.java │ │ ├── RequestOptionsTest.java │ │ ├── encoder │ │ ├── StringEncoderTest.java │ │ └── StringRequestEntityTest.java │ │ ├── http │ │ ├── HttpExceptionTest.java │ │ └── HttpHeaderTest.java │ │ ├── support │ │ └── AuditingExecutor.java │ │ ├── assertions │ │ ├── HttpHeaderAssert.java │ │ ├── HttpRequestAssert.java │ │ ├── HttpResponseAssert.java │ │ └── Assertions.java │ │ ├── decoder │ │ ├── StringDecoderTest.java │ │ └── AbstractResponseDecoderTest.java │ │ └── TargetMethodDefinitionTest.java │ └── main │ └── java │ └── feign │ ├── http │ ├── HttpMethod.java │ └── HttpException.java │ ├── template │ ├── Chunk.java │ ├── expander │ │ ├── DefaultExpander.java │ │ ├── ExpanderUtils.java │ │ ├── SimpleExpander.java │ │ ├── ListExpander.java │ │ └── MapExpander.java │ ├── TemplateParameter.java │ ├── Literal.java │ ├── ReservedExpansionPolicy.java │ ├── SimpleExpansionPolicy.java │ ├── ExpressionExpander.java │ ├── FragmentExpansionPolicy.java │ ├── DotExpansionPolicy.java │ ├── FormStyleExpansionPolicy.java │ ├── PathStyleExpansionPolicy.java │ ├── PathSegmentExpansionPolicy.java │ ├── FormContinuationStyleExpansionPolicy.java │ ├── ExpanderRegistry.java │ ├── ExpansionPolicy.java │ └── SimpleTemplateParameter.java │ ├── Retry.java │ ├── TargetMethodHandler.java │ ├── retry │ ├── RetryInterval.java │ ├── NoRetry.java │ ├── ExceptionCondition.java │ ├── RetryContext.java │ ├── BackoffRetryInterval.java │ ├── StatusCodeCondition.java │ └── RetryCondition.java │ ├── contract │ ├── Body.java │ ├── impl │ │ ├── BodyAnnotationProcessor.java │ │ ├── HeaderAnnotationProcessor.java │ │ ├── HeadersAnnotationProcessor.java │ │ ├── ParamAnnotationProcessor.java │ │ └── RequestAnnotationProcessor.java │ ├── Headers.java │ ├── AnnotationProcessor.java │ ├── Param.java │ ├── Header.java │ ├── ParameterAnnotationProcessor.java │ ├── Request.java │ └── FeignContract.java │ ├── Client.java │ ├── RequestInterceptor.java │ ├── Target.java │ ├── Contract.java │ ├── impl │ ├── type │ │ ├── ClassTypeDefinition.java │ │ ├── GenericArrayTypeDefinition.java │ │ ├── AbstractTypeDefinition.java │ │ ├── TypeDefinition.java │ │ ├── WildCardTypeDefinition.java │ │ ├── ParameterizedTypeDefinition.java │ │ └── TypeUtils.java │ ├── AbsoluteUriTarget.java │ ├── AsyncTargetMethodHandler.java │ ├── TypeDrivenMethodHandlerFactory.java │ └── BlockingTargetMethodHandler.java │ ├── ResponseDecoder.java │ ├── Header.java │ ├── RequestEncoder.java │ ├── FeignTarget.java │ ├── TargetMethodHandlerFactory.java │ ├── encoder │ ├── StringEncoder.java │ └── StringRequestEntity.java │ ├── ExceptionHandler.java │ ├── RequestEntity.java │ ├── annotation │ └── FeignTargetAnnotationProcessor.java │ ├── Logger.java │ ├── decoder │ ├── StringDecoder.java │ └── AbstractResponseDecoder.java │ ├── exception │ └── FeignException.java │ ├── Request.java │ ├── Response.java │ ├── proxy │ ├── ProxyFeign.java │ └── GuardMethodHandler.java │ ├── support │ ├── StringUtils.java │ └── Assert.java │ └── FeignConfiguration.java ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── tests ├── src │ └── main │ │ ├── resources │ │ └── META-INF │ │ │ └── services │ │ │ └── javax.annotation.processing.Processor │ │ └── java │ │ └── io │ │ └── openfeign │ │ └── TestService.java └── pom.xml ├── .github ├── pull_request_template.md └── ISSUE_TEMPLATE │ ├── test-case.md │ ├── feature_request.md │ └── bug_report.md ├── src └── resources │ ├── header-definition.xml │ └── NOTICE ├── NOTICE ├── .circleci └── config.yml ├── README.md └── .gitignore /core/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker: -------------------------------------------------------------------------------- 1 | mock-maker-inline -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OpenFeign/feignx/HEAD/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /tests/src/main/resources/META-INF/services/javax.annotation.processing.Processor: -------------------------------------------------------------------------------- 1 | feign.annotation.FeignTargetAnnotationProcessor -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.5.4/apache-maven-3.5.4-bin.zip 2 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # . 2 | 3 | Summary of changes 4 | 5 | Detailed explanation of your changes. Included any assumptions you've made or any additional context for reviewers. 6 | 7 | Changes proposed: 8 | 9 | - 10 | - 11 | - 12 | -------------------------------------------------------------------------------- /core/src/test/resources/template/feign-tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "Feign Specific Examples: Complex Variable Names": { 3 | "level": 4, 4 | "variables": { 5 | "list[]": [ 6 | "red", 7 | "green", 8 | "blue" 9 | ], 10 | "$data": "data" 11 | }, 12 | "testcases": [ 13 | [ 14 | "{?list[]}", 15 | "?list%5B%5D=red,green,blue" 16 | ], 17 | [ 18 | "{?list[]*}", 19 | "?list%5B%5D=red&list%5B%5D=green&list%5B%5D=blue" 20 | ] 21 | ] 22 | } 23 | } -------------------------------------------------------------------------------- /src/resources/header-definition.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | /* 5 | * 6 | */EOL 7 | true 8 | (\s|\t)*/\*.*$ 9 | .*\*/(\s|\t)*$ 10 | true 11 | true 12 | false 13 | 14 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | Copyright 2019-2020 OpenFeign Contributors 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/test-case.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Test case 3 | about: Describe a scenario we should test for 4 | title: '' 5 | labels: test case 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Scenario:** A short description of the situation. 11 | 12 | **Given:** a description of the current state, including any entity involved 13 | **And:** ... 14 | 15 | **When:** an event or change occurs. This defined the action taking place. 16 | **And:** ... 17 | 18 | **Then:** the state after the event. This defines the expected outcome of the test. 19 | 20 | **Additional Context** 21 | Any additional context you would like to provide. 22 | -------------------------------------------------------------------------------- /src/resources/NOTICE: -------------------------------------------------------------------------------- 1 | Copyright ${license.git.copyrightYears} OpenFeign Contributors 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Environment (please complete the following information):** 24 | - Version [e.g. 22] 25 | - JDK Version [e.g. 8, 11] 26 | - Extensions [e.g. Spring Cloud, OkHttp] 27 | 28 | **Additional context** 29 | Add any other context about the problem here. 30 | -------------------------------------------------------------------------------- /core/src/main/java/feign/http/HttpMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.http; 18 | 19 | /** 20 | * Http Methods. 21 | */ 22 | public enum HttpMethod { 23 | GET, 24 | POST, 25 | PUT, 26 | PATCH, 27 | DELETE, 28 | OPTIONS, 29 | CONNECT, 30 | TRACE 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/Chunk.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Part of a {@link UriTemplate}. 21 | */ 22 | public interface Chunk { 23 | 24 | /** 25 | * Value of the Chunk. 26 | * 27 | * @return chunk value. 28 | */ 29 | String getValue(); 30 | 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/feign/Retry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import java.util.function.Function; 20 | 21 | /** 22 | * Retry Handler interface. 23 | */ 24 | public interface Retry { 25 | 26 | Response execute( 27 | String methodName, Request request, Function callback) throws Throwable; 28 | 29 | } 30 | -------------------------------------------------------------------------------- /core/src/test/java/feign/template/UriUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class UriUtilsTest { 24 | 25 | @Test 26 | void skipEncoding_whenAlreadyEncoded() { 27 | String result = UriUtils.encode("%25"); 28 | assertThat(result).isEqualTo("%25"); 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /core/src/test/java/feign/exception/FeignExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.exception; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class FeignExceptionTest { 24 | 25 | @Test 26 | void getMethod() { 27 | FeignException exception = new FeignException("message", "method"); 28 | assertThat(exception.getMethod()).isEqualTo("method"); 29 | } 30 | } -------------------------------------------------------------------------------- /core/src/test/java/feign/template/expander/DefaultExpanderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.expander; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class DefaultExpanderTest { 24 | 25 | @Test 26 | void cannotBeUsed() { 27 | DefaultExpander expander = new DefaultExpander(); 28 | assertThrows(UnsupportedOperationException.class, () -> expander.expand(null, null)); 29 | } 30 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/TargetMethodHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | /** 20 | * Functional Handler for a method found on a given Target. 21 | */ 22 | public interface TargetMethodHandler { 23 | 24 | /** 25 | * Handle the method execution. 26 | * 27 | * @param args to apply. 28 | * @return the result of the operation. 29 | * @throws Throwable if the operation failed. 30 | */ 31 | Object execute(Object[] args) throws Throwable; 32 | 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/feign/retry/RetryInterval.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | /** 20 | * Defines a Specification for determining the interval between retry attempts. 21 | */ 22 | public interface RetryInterval { 23 | 24 | /** 25 | * Determine the retry interval. 26 | * 27 | * @param retryContext for the retry event. 28 | * @return the amount of time, in milliseconds to wait. 29 | */ 30 | long getInterval(RetryContext retryContext); 31 | 32 | } 33 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # Java Maven CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-java/ for more details 4 | # 5 | 6 | # our job defaults 7 | defaults: &defaults 8 | docker: 9 | - image: circleci/openjdk:11.0.2 10 | working_directory: ~/feignx 11 | environment: 12 | # Customize the JVM maximum heap limit 13 | MAVEN_OPTS: -Xmx3200m 14 | 15 | version: 2.1 16 | orbs: 17 | codecov: codecov/codecov@1.1.1 18 | jobs: 19 | build: 20 | <<: *defaults 21 | steps: 22 | - checkout 23 | - restore_cache: 24 | keys: 25 | - feignx-dependencies-{{ checksum "pom.xml" }} 26 | - feignx-dependencies- 27 | - run: mvn dependency:resolve-plugins go-offline:resolve-dependencies install -DskipTests=true 28 | - save_cache: 29 | paths: 30 | - ~/.m2 31 | key: feignx-dependencies-{{ checksum "pom.xml" }} 32 | - run: mvn -o test 33 | - store_test_results: 34 | path: core/target/surefire-reports 35 | - codecov/upload: 36 | file: tests/target/site/jacoco-aggregate/jacoco.xml 37 | workflows: 38 | version: 2 39 | build: 40 | jobs: 41 | - build -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/Body.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | 26 | /** 27 | * Annotation that marks a given method parameter as the HttpRequest Body. 28 | */ 29 | @Target(ElementType.PARAMETER) 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Documented 32 | public @interface Body { 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/test/java/feign/template/ExpressionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | import static org.junit.jupiter.api.Assertions.*; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class ExpressionsTest { 24 | 25 | @Test 26 | void emptyExpressions_areRejected() { 27 | assertThrows(IllegalArgumentException.class, () -> Expressions.create("{}")); 28 | } 29 | 30 | @Test 31 | void invalidExpressions_areRejected() { 32 | assertThrows(IllegalArgumentException.class, () -> Expressions.create("abc")); 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/Client.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.exception.FeignException; 20 | 21 | /** 22 | * Target Client instance responsible for submitting the Request to the Target and processing 23 | * the Response. 24 | */ 25 | public interface Client { 26 | 27 | /** 28 | * Submit the Request to the Target. 29 | * 30 | * @param request to be sent. 31 | * @return a Response for the Request. 32 | * @throws FeignException if any errors occur. 33 | */ 34 | Response request(Request request) throws FeignException; 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/feign/RequestInterceptor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.http.RequestSpecification; 20 | import java.util.function.Function; 21 | 22 | /** 23 | * Function used to act on a {@link RequestSpecification} during request processing. It is possible 24 | * to return an entirely new {@link RequestSpecification} from this component. 25 | */ 26 | public interface RequestInterceptor extends Function { 27 | 28 | static RequestInterceptor identity() { 29 | return (t) -> t; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/feign/retry/NoRetry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | import feign.Request; 20 | import feign.Response; 21 | import feign.Retry; 22 | import java.util.function.Function; 23 | 24 | /** 25 | * Executes the callback without any retries. 26 | */ 27 | public class NoRetry implements Retry { 28 | 29 | @Override 30 | public Response execute( 31 | String methodName, Request request, Function callback) { 32 | /* execute the callback with no retry support */ 33 | return callback.apply(request); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/expander/DefaultExpander.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.expander; 18 | 19 | import feign.template.ExpressionExpander; 20 | import feign.template.ExpressionVariable; 21 | 22 | /** 23 | * Default Expression Expander used as a marker to use our internal expander resolution. 24 | */ 25 | public class DefaultExpander implements ExpressionExpander { 26 | 27 | @Override 28 | public String expand(ExpressionVariable variable, Object value) { 29 | throw new UnsupportedOperationException("expansion is not supported."); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/java/feign/Target.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.http.RequestSpecification; 20 | 21 | /** 22 | * Represents a Target interface, containing the service definition. 23 | * 24 | * @param type of the Target. 25 | */ 26 | public interface Target { 27 | 28 | /** 29 | * Interface Type for this Target. 30 | * 31 | * @return the service definition type. 32 | */ 33 | Class type(); 34 | 35 | /** 36 | * Short descriptive name for this Target. 37 | * 38 | * @return target name. 39 | */ 40 | String name(); 41 | 42 | } 43 | -------------------------------------------------------------------------------- /tests/src/main/java/io/openfeign/TestService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package io.openfeign; 18 | 19 | import feign.FeignTarget; 20 | import feign.contract.Param; 21 | import feign.contract.Request; 22 | import feign.http.HttpMethod; 23 | import java.util.List; 24 | 25 | /** 26 | * Example Service used for Annotation Processor testing. 27 | */ 28 | @FeignTarget 29 | public interface TestService { 30 | 31 | @Request(method = HttpMethod.GET, value = "/contributors/{repository}") 32 | List getContributors(@Param("repository") String repository); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /core/src/test/java/feign/template/tck/TestExamples.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.tck; 18 | 19 | import java.util.LinkedHashMap; 20 | import java.util.Map; 21 | 22 | public class TestExamples { 23 | 24 | private Map testGroups; 25 | 26 | protected TestExamples() { 27 | super(); 28 | this.testGroups = new LinkedHashMap<>(); 29 | } 30 | 31 | public Map getTestGroups() { 32 | return testGroups; 33 | } 34 | 35 | public void setTestGroups( 36 | Map testGroups) { 37 | this.testGroups = testGroups; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/feign/Contract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.contract.TargetDefinition; 20 | 21 | /** 22 | * Represents the agreement between the specific implementation and the user, enforcing how 23 | * each Target method can be defined. 24 | */ 25 | public interface Contract { 26 | 27 | /** 28 | * Create a new {@link TargetDefinition} from the {@link FeignConfiguration} provided. 29 | * 30 | * @param targetType with the Target configuration. 31 | * @return a new {@link TargetDefinition} instance. 32 | */ 33 | TargetDefinition apply(Class targetType, FeignConfiguration configuration); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/type/ClassTypeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl.type; 18 | 19 | /** 20 | * Type Definition for a concrete Class. 21 | */ 22 | public class ClassTypeDefinition extends AbstractTypeDefinition { 23 | 24 | private final Class type; 25 | 26 | /** 27 | * Creates a new ClassTypeDefinition. 28 | * 29 | * @param type to wrap. 30 | */ 31 | ClassTypeDefinition(Class type) { 32 | this.type = type; 33 | } 34 | 35 | /** 36 | * The concrete type. 37 | * 38 | * @return the wrapped type. 39 | */ 40 | @Override 41 | public Class getType() { 42 | return this.type; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/impl/BodyAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract.impl; 18 | 19 | import feign.contract.Body; 20 | import feign.contract.ParameterAnnotationProcessor; 21 | import feign.contract.TargetMethodDefinition.Builder; 22 | 23 | /** 24 | * Annotation Processor for the {@link Body} annotation. Registers which method parameters contains 25 | * the request body. 26 | */ 27 | public class BodyAnnotationProcessor implements ParameterAnnotationProcessor { 28 | 29 | @Override 30 | public void process(Body annotation, String name, Integer index, String type, Builder builder) { 31 | builder.body(index); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /core/src/main/java/feign/ResponseDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | /** 20 | * A component that can parse a given Response and "decode" the Response body into the 21 | * type desired. 22 | */ 23 | @FunctionalInterface 24 | public interface ResponseDecoder { 25 | 26 | /** 27 | * Decode the Response Body into the desired type. 28 | * 29 | * @param response to decode. 30 | * @param type instance desired. 31 | * @param desired type. 32 | * @return an instance of the desired type. 33 | * @throws feign.exception.FeignException if the response could not be decoded. 34 | */ 35 | T decode(Response response, Class type); 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/TemplateParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Represents a {@link UriTemplate} parameter to be resolved. 21 | */ 22 | public interface TemplateParameter { 23 | 24 | /** 25 | * Name of the parameter. Matches up with the variable name defined in an Expression. 26 | * 27 | * @return the parameter name. 28 | */ 29 | String name(); 30 | 31 | /** 32 | * {@link ExpressionExpander} instance to use when this parameter is expanded as part of a 33 | * uri template. 34 | * 35 | * @return an {@link ExpressionExpander} instance. 36 | */ 37 | ExpressionExpander expander(); 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/Literal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | import feign.support.Assert; 20 | 21 | /** 22 | * Chunk for static values. 23 | */ 24 | public class Literal implements Chunk { 25 | 26 | private final String value; 27 | 28 | /** 29 | * Creates a new Literal Chunk. 30 | * 31 | * @param value of the chunk. 32 | */ 33 | Literal(String value) { 34 | Assert.isNotEmpty(value, "value is required."); 35 | this.value = value; 36 | } 37 | 38 | /** 39 | * Chunk Value. 40 | * 41 | * @return the literal value. 42 | */ 43 | @Override 44 | public String getValue() { 45 | return this.value; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/java/feign/Header.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import java.util.Collection; 20 | 21 | /** 22 | * Represents a Header property for a Targeted Request. 23 | */ 24 | public interface Header { 25 | 26 | /** 27 | * Name of the Header. 28 | * 29 | * @return header name. 30 | */ 31 | String name(); 32 | 33 | /** 34 | * Values of the Header. 35 | * 36 | * @return header values. 37 | */ 38 | Collection values(); 39 | 40 | /** 41 | * Add a Value to this Header. 42 | * 43 | * @param value to add. 44 | */ 45 | void value(String value); 46 | 47 | /** 48 | * Clear the Header values. 49 | */ 50 | void clear(); 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/feign/RequestEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.http.RequestSpecification; 20 | 21 | /** 22 | * Prepares a {@link RequestEntity} for use. 23 | */ 24 | @FunctionalInterface 25 | public interface RequestEncoder { 26 | 27 | /** 28 | * Encode the request content for use. May return {@code null} if the entity should be 29 | * ignored. 30 | * 31 | * @param content to be encoded. 32 | * @param requestSpecification containing the current {@link RequestSpecification}. 33 | * @return a {@link RequestEntity} instance, or {@code null}. 34 | */ 35 | RequestEntity apply(Object content, RequestSpecification requestSpecification); 36 | 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/feign/FeignTarget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Inherited; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | 26 | /** 27 | * Declares a given interface a Feign {@link feign.Target}. Used by our annotation processor 28 | * to generate target implementations at compile time. 29 | */ 30 | @Target({ElementType.TYPE}) 31 | @Retention(RetentionPolicy.CLASS) 32 | @Documented 33 | @Inherited 34 | public @interface FeignTarget { 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/ReservedExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Expression that allows for characters in the Reserved to be included, without encoding. 21 | */ 22 | class ReservedExpansionPolicy extends ExpansionPolicy { 23 | private static final ReservedExpansionPolicy instance = new ReservedExpansionPolicy(); 24 | 25 | /** 26 | * Return a singleton instance of this Expansion Policy. 27 | * 28 | * @return expansion policy instance 29 | */ 30 | public static ReservedExpansionPolicy getInstance() { 31 | return instance; 32 | } 33 | 34 | private ReservedExpansionPolicy() { 35 | super("", ",", "", false, true); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/feign/TargetMethodHandlerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.contract.TargetMethodDefinition; 20 | 21 | /** 22 | * Factory that creates TargetMethodHandler instances. 23 | */ 24 | public interface TargetMethodHandlerFactory { 25 | 26 | /** 27 | * Creates a new TargetMethodHandler, using the definition and configuration provided. 28 | * 29 | * @param targetMethodDefinition for the method to handle. 30 | * @param feignConfiguration with the shared configuration. 31 | * @return a TargetMethodHandler instance. 32 | */ 33 | TargetMethodHandler create(TargetMethodDefinition targetMethodDefinition, 34 | FeignConfiguration feignConfiguration); 35 | 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/feign/encoder/StringEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.encoder; 18 | 19 | import feign.RequestEncoder; 20 | import feign.RequestEntity; 21 | import feign.http.RequestSpecification; 22 | 23 | /** 24 | * HttpRequest Encoder that encodes the request content as a String. This implementation uses 25 | * the {@code toString()} method on the content object to encode the data. 26 | */ 27 | public class StringEncoder implements RequestEncoder { 28 | 29 | @Override 30 | public RequestEntity apply(Object content, RequestSpecification requestSpecification) { 31 | if (content != null) { 32 | return new StringRequestEntity(content.toString()); 33 | } 34 | return null; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/SimpleExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Expression that expands object into a single value, pct-encoding all values not in the 21 | * unreserved set. 22 | */ 23 | class SimpleExpansionPolicy extends ExpansionPolicy { 24 | private static final SimpleExpansionPolicy instance = new SimpleExpansionPolicy(); 25 | 26 | /** 27 | * Return a singleton instance of this Expansion Policy. 28 | * 29 | * @return expansion policy instance 30 | */ 31 | public static SimpleExpansionPolicy getInstance() { 32 | return instance; 33 | } 34 | 35 | private SimpleExpansionPolicy() { 36 | super("", ",", "", false, false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/test/java/feign/retry/NoRetryTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | import static org.mockito.ArgumentMatchers.any; 20 | import static org.mockito.Mockito.mock; 21 | import static org.mockito.Mockito.times; 22 | import static org.mockito.Mockito.verify; 23 | 24 | import feign.Client; 25 | import feign.Request; 26 | import feign.Retry; 27 | import org.junit.jupiter.api.Test; 28 | 29 | class NoRetryTest { 30 | 31 | @Test 32 | void callbackShouldBeCalled() throws Throwable { 33 | Retry retry = new NoRetry(); 34 | Client client = mock(Client.class); 35 | retry.execute("test", mock(Request.class), (client::request)); 36 | verify(client, times(1)).request(any(Request.class)); 37 | } 38 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/template/ExpressionExpander.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Manages the expansion of a given Expression. Implementations are expected to provide a 21 | * default, no-argument constructor and be thread-safe, to encourage lazy initialization and reuse 22 | * between {@link feign.Target}s. 23 | */ 24 | public interface ExpressionExpander { 25 | 26 | /** 27 | * Expand the given expression, using the value provided. 28 | * 29 | * @param variable to expand. 30 | * @param value containing the variable values. 31 | * @return the expanded result, an empty string, or {@literal null}. 32 | */ 33 | String expand(ExpressionVariable variable, Object value); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/FragmentExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Fragment based expression. Assumes the same capability as a reserved expression, but prefixed 21 | * with a {@literal #} 22 | */ 23 | class FragmentExpansionPolicy extends ExpansionPolicy { 24 | private static final FragmentExpansionPolicy instance = new FragmentExpansionPolicy(); 25 | 26 | /** 27 | * Return a singleton instance of this Expansion Policy. 28 | * 29 | * @return expansion policy instance 30 | */ 31 | public static FragmentExpansionPolicy getInstance() { 32 | return instance; 33 | } 34 | 35 | private FragmentExpansionPolicy() { 36 | super("#", ",", "", false, true); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/Headers.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Inherited; 22 | import java.lang.annotation.Retention; 23 | import java.lang.annotation.RetentionPolicy; 24 | import java.lang.annotation.Target; 25 | 26 | /** 27 | * Annotation that represents a list of Http HttpHeader. 28 | */ 29 | @Target({ElementType.TYPE, ElementType.METHOD}) 30 | @Retention(RetentionPolicy.RUNTIME) 31 | @Documented 32 | @Inherited 33 | public @interface Headers { 34 | 35 | /** 36 | * List of HttpHeader. 37 | * 38 | * @return a list of Header values. 39 | */ 40 | Header[] value() default {}; 41 | 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/impl/HeaderAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract.impl; 18 | 19 | import feign.contract.AnnotationProcessor; 20 | import feign.contract.Header; 21 | import feign.contract.TargetMethodDefinition.Builder; 22 | import feign.http.HttpHeader; 23 | 24 | /** 25 | * Annotation processor for the {@link Header} annotation. Evaluates and prepares an HTTP 26 | * Header. 27 | */ 28 | public class HeaderAnnotationProcessor implements AnnotationProcessor
{ 29 | 30 | @Override 31 | public void process(Header annotation, Builder builder) { 32 | HttpHeader httpHeader = new HttpHeader(annotation.name()); 33 | httpHeader.value(annotation.value()); 34 | builder.header(httpHeader); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/DotExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Expression that uses the dot {@code .} character as a prefix and exploded delimiter, allowing 21 | * for expansion of domain names and other dot like values on a URI. 22 | */ 23 | class DotExpansionPolicy extends ExpansionPolicy { 24 | private static final DotExpansionPolicy instance = new DotExpansionPolicy(); 25 | 26 | /** 27 | * Return a singleton instance of this Expansion Policy. 28 | * 29 | * @return expansion policy instance 30 | */ 31 | public static DotExpansionPolicy getInstance() { 32 | return instance; 33 | } 34 | 35 | private DotExpansionPolicy() { 36 | super(".", ".", "", false, false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/FormStyleExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Expression that expands values using a Query String {@code ?} and {@code &} style, 21 | * allowing for expansion of query parameters. 22 | */ 23 | class FormStyleExpansionPolicy extends ExpansionPolicy { 24 | private static final FormStyleExpansionPolicy instance = new FormStyleExpansionPolicy(); 25 | 26 | /** 27 | * Return a singleton instance of this Expansion Policy. 28 | * 29 | * @return expansion policy instance 30 | */ 31 | public static FormStyleExpansionPolicy getInstance() { 32 | return instance; 33 | } 34 | 35 | private FormStyleExpansionPolicy() { 36 | super("?", "&", "=", true, false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/PathStyleExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Expression that use the semi-colon {@code ;} character as a prefix and exploded delimiter, 21 | * allowing for expansion of path parameters. 22 | */ 23 | class PathStyleExpansionPolicy extends ExpansionPolicy { 24 | private static final PathStyleExpansionPolicy instance = new PathStyleExpansionPolicy(); 25 | 26 | /** 27 | * Return a singleton instance of this Expansion Policy. 28 | * 29 | * @return expansion policy instance 30 | */ 31 | public static PathStyleExpansionPolicy getInstance() { 32 | return instance; 33 | } 34 | 35 | private PathStyleExpansionPolicy() { 36 | super(";", ";", "", true, false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/PathSegmentExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Expression that use the slash {@code /} character as a prefix and exploded delimiter, allowing 21 | * for expansion of path segments. 22 | */ 23 | class PathSegmentExpansionPolicy extends ExpansionPolicy { 24 | private static final PathSegmentExpansionPolicy instance = new PathSegmentExpansionPolicy(); 25 | 26 | /** 27 | * Return a singleton instance of this Expansion Policy. 28 | * 29 | * @return expansion policy instance 30 | */ 31 | public static PathSegmentExpansionPolicy getInstance() { 32 | return instance; 33 | } 34 | 35 | private PathSegmentExpansionPolicy() { 36 | super("/", "/", "", false, false); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /core/src/test/java/feign/retry/ExceptionConditionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import java.io.IOException; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class ExceptionConditionTest { 25 | 26 | ExceptionCondition condition = new ExceptionCondition(IOException.class); 27 | 28 | @Test 29 | void shouldPass_ifExceptionMatches() { 30 | RetryContext context = new RetryContext(1, new IOException("stuck"), null); 31 | assertThat(condition.test(context)).isTrue(); 32 | } 33 | 34 | @Test 35 | void shouldFail_ifExceptionDoesNotMatch() { 36 | RetryContext context = new RetryContext(1, new RuntimeException("stuck"), null); 37 | assertThat(condition.test(context)).isFalse(); 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/template/FormContinuationStyleExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Expression that expands values that represent the continuation of a Query String, 21 | * allowing for expansion of additional query parameters. 22 | */ 23 | class FormContinuationStyleExpansionPolicy extends ExpansionPolicy { 24 | private static final FormContinuationStyleExpansionPolicy instance = 25 | new FormContinuationStyleExpansionPolicy(); 26 | 27 | /** 28 | * Return a singleton instance of this Expansion Policy. 29 | * 30 | * @return expansion policy instance 31 | */ 32 | public static FormContinuationStyleExpansionPolicy getInstance() { 33 | return instance; 34 | } 35 | 36 | private FormContinuationStyleExpansionPolicy() { 37 | super("&", "&", "=", true, false); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /core/src/test/java/feign/RequestOptionsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class RequestOptionsTest { 24 | 25 | @Test 26 | void isEqual_byHashCode() { 27 | RequestOptions options = RequestOptions.builder().build(); 28 | RequestOptions duplicate = RequestOptions.builder().build(); 29 | assertThat(options.hashCode()).isEqualTo(duplicate.hashCode()); 30 | } 31 | 32 | @Test 33 | void isEqual_toItself() { 34 | RequestOptions options = RequestOptions.builder().build(); 35 | assertThat(options).isEqualTo(options); 36 | } 37 | 38 | @Test 39 | void isNotEqual_toOtherTypes() { 40 | RequestOptions options = RequestOptions.builder().build(); 41 | assertThat(options).isNotEqualTo("A String"); 42 | } 43 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/AnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract; 18 | 19 | import java.lang.annotation.Annotation; 20 | 21 | /** 22 | * Annotation Processor interface. Used by Annotation Driven Contracts to process annotated methods 23 | * and classes. These processors are designed to be used in both reflective and compile-time modes. 24 | * 25 | * @param Supported annotation type. 26 | */ 27 | public interface AnnotationProcessor { 28 | 29 | /** 30 | * Evaluate and process the provided annotation, updating the provided builder. Implementations 31 | * are expected to update builder via side-effects. 32 | * 33 | * @param annotation to evaluate. 34 | * @param builder with the current method context. 35 | */ 36 | void process(T annotation, TargetMethodDefinition.Builder builder); 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/expander/ExpanderUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.expander; 18 | 19 | import java.util.Map; 20 | 21 | /** 22 | * Utilities related to variable expansion. 23 | */ 24 | class ExpanderUtils { 25 | 26 | /** 27 | * Determines if the provided Class is simple. That is, something that is 28 | * not a core primitive, enum, annotation, array, String, Iterable, or Map. 29 | * 30 | * @param type to be evaluated. 31 | * @return {@literal true} if the type is considered simple, {@literal false} otherwise. 32 | */ 33 | static boolean isSimpleType(Class type) { 34 | return type.isAnnotation() || type.isArray() || type.isEnum() || type.isPrimitive() 35 | || String.class == type || Iterable.class.isAssignableFrom(type) 36 | || Map.class.isAssignableFrom(type); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/feign/ExceptionHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import java.util.function.Function; 20 | 21 | /** 22 | * Consumer responsible for processing any Exceptions that occur during method processing. 23 | */ 24 | public interface ExceptionHandler extends Function { 25 | 26 | /** 27 | * Throws a new RuntimeException based on the exception received. 28 | * 29 | * @param throwable to throw again. 30 | */ 31 | @Override 32 | default RuntimeException apply(Throwable throwable) { 33 | /* always rethrow */ 34 | if (throwable instanceof RuntimeException) { 35 | return (RuntimeException) throwable; 36 | } 37 | return new RuntimeException(throwable); 38 | } 39 | 40 | /** 41 | * Exception Handler that wraps and throws any exceptions. 42 | */ 43 | class RethrowExceptionHandler implements ExceptionHandler { 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /core/src/main/java/feign/RequestEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import java.nio.charset.Charset; 20 | import java.util.Optional; 21 | 22 | /** 23 | * Represents the content of a {@link Request}. 24 | */ 25 | public interface RequestEntity { 26 | 27 | /** 28 | * Character Set this entity is encoded in. 29 | * 30 | * @return the entity {@link Charset}. 31 | */ 32 | Optional getCharset(); 33 | 34 | /** 35 | * Length of the entity data. 36 | * 37 | * @return entity data length. 38 | */ 39 | int getContentLength(); 40 | 41 | /** 42 | * Content Type of the entity. 43 | * 44 | * @return String representation of a MIME-Type or any other acceptable Content Type. 45 | */ 46 | String getContentType(); 47 | 48 | /** 49 | * Entity Data. 50 | * 51 | * @return entity data. 52 | */ 53 | byte[] getData(); 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/java/feign/annotation/FeignTargetAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.annotation; 18 | 19 | import java.util.Set; 20 | import javax.annotation.processing.AbstractProcessor; 21 | import javax.annotation.processing.RoundEnvironment; 22 | import javax.annotation.processing.SupportedAnnotationTypes; 23 | import javax.annotation.processing.SupportedSourceVersion; 24 | import javax.lang.model.SourceVersion; 25 | import javax.lang.model.element.TypeElement; 26 | 27 | /** 28 | * Annotation Processor for {@link feign.FeignTarget} annotated interfaces. 29 | */ 30 | @SupportedAnnotationTypes({"feign.FeignTarget"}) 31 | @SupportedSourceVersion(SourceVersion.RELEASE_11) 32 | public class FeignTargetAnnotationProcessor extends AbstractProcessor { 33 | 34 | @Override 35 | public boolean process(Set annotations, RoundEnvironment roundEnv) { 36 | return false; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/Param.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract; 18 | 19 | import feign.template.ExpressionExpander; 20 | import feign.template.expander.DefaultExpander; 21 | import java.lang.annotation.Documented; 22 | import java.lang.annotation.ElementType; 23 | import java.lang.annotation.Retention; 24 | import java.lang.annotation.RetentionPolicy; 25 | import java.lang.annotation.Target; 26 | 27 | /** 28 | * Annotation that represents a template parameter. 29 | */ 30 | @Target({ElementType.PARAMETER, ElementType.TYPE, ElementType.METHOD}) 31 | @Retention(RetentionPolicy.RUNTIME) 32 | @Documented 33 | public @interface Param { 34 | 35 | /** 36 | * Name of the Parameter. 37 | * 38 | * @return parameter name. 39 | */ 40 | String value(); 41 | 42 | /** 43 | * Expander instance to use. 44 | * 45 | * @return the expression expander type. 46 | */ 47 | Class expander() default DefaultExpander.class; 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/feign/Logger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.retry.RetryContext; 20 | 21 | /** 22 | * Logger component responsible for log messages pertaining to specific components involved 23 | * in the Request/Response process. 24 | */ 25 | public interface Logger { 26 | 27 | /** 28 | * Log the {@link Request}. 29 | * 30 | * @param methodName of the method making the request. 31 | * @param request to log. 32 | */ 33 | void logRequest(String methodName, Request request); 34 | 35 | /** 36 | * Log the {@link Response}. 37 | * 38 | * @param methodName of the method making the request. 39 | * @param response to log. 40 | */ 41 | void logResponse(String methodName, Response response); 42 | 43 | /** 44 | * Log the {@link RetryContext} used during a retry. 45 | * 46 | * @param methodName of the method making the request. 47 | * @param context to log. 48 | */ 49 | void logRetry(String methodName, RetryContext context); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/Header.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Inherited; 22 | import java.lang.annotation.Repeatable; 23 | import java.lang.annotation.Retention; 24 | import java.lang.annotation.RetentionPolicy; 25 | import java.lang.annotation.Target; 26 | 27 | /** 28 | * Annotation that represents a Http HttpRequest Header. 29 | */ 30 | @Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER}) 31 | @Retention(RetentionPolicy.RUNTIME) 32 | @Documented 33 | @Inherited 34 | @Repeatable(Headers.class) 35 | public @interface Header { 36 | 37 | /** 38 | * Name of the Header. Examples are {@literal Content-Type} and {@literal Authorization} 39 | * 40 | * @return header name. 41 | */ 42 | String name(); 43 | 44 | /** 45 | * Value of the Header. Must be a literal. 46 | * 47 | * @return header value. 48 | */ 49 | String value(); 50 | 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/ExpanderRegistry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Factory for creating {@link ExpressionExpander} instances. 21 | */ 22 | public interface ExpanderRegistry { 23 | 24 | /** 25 | * Retrieves an {@link ExpressionExpander} based on the type. 26 | * 27 | * @param type of the value to be expanded. 28 | * @return an {@link ExpressionExpander} instance. 29 | * @throws IllegalStateException if the expander instance could not be created. 30 | */ 31 | ExpressionExpander getExpanderByType(Class type); 32 | 33 | /** 34 | * Retrieves an {@link ExpressionExpander} instance from the expander type provided. 35 | * 36 | * @param expanderClass to retrieve. 37 | * @return an {@link ExpressionExpander} instance of the type provided. 38 | * @throws IllegalStateException if the expander instance could be created. 39 | */ 40 | ExpressionExpander getExpander(Class expanderClass, 41 | String typeClassName); 42 | } 43 | -------------------------------------------------------------------------------- /core/src/test/java/feign/retry/StatusCodeConditionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import static org.mockito.Mockito.mock; 21 | import static org.mockito.Mockito.when; 22 | 23 | import feign.Response; 24 | import org.junit.jupiter.api.Test; 25 | 26 | class StatusCodeConditionTest { 27 | 28 | private StatusCodeCondition condition = new StatusCodeCondition(503); 29 | 30 | @Test 31 | void shouldPass_ifCodeMatches() { 32 | Response response = mock(Response.class); 33 | when(response.status()).thenReturn(503); 34 | 35 | RetryContext context = new RetryContext(1, null, response); 36 | assertThat(condition.test(context)).isTrue(); 37 | } 38 | 39 | @Test 40 | void shouldFail_ifCodeDoesNotMatch() { 41 | Response response = mock(Response.class); 42 | when(response.status()).thenReturn(200); 43 | 44 | RetryContext context = new RetryContext(1, null, response); 45 | assertThat(condition.test(context)).isFalse(); 46 | } 47 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/decoder/StringDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.decoder; 18 | 19 | import feign.Response; 20 | import java.io.IOException; 21 | import java.nio.charset.StandardCharsets; 22 | 23 | /** 24 | * Response Decoder that reads the response buffer and converts it to a 25 | * UTF-8 String. 26 | */ 27 | public class StringDecoder extends AbstractResponseDecoder { 28 | 29 | @SuppressWarnings("unchecked") 30 | @Override 31 | protected T decodeInternal(Response response, Class type) { 32 | if (String.class.equals(type)) { 33 | try { 34 | /* ready the body into a string */ 35 | return (T) new String(response.toByteArray(), StandardCharsets.UTF_8); 36 | } catch (IOException ioe) { 37 | throw new IllegalStateException("Error occurred reading response: " + ioe, ioe); 38 | } 39 | } 40 | throw new IllegalArgumentException("Error occurred while decoding the Response, " 41 | + "type: " + type.getSimpleName() + " is not supported by this decoder."); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/ParameterAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract; 18 | 19 | import java.lang.annotation.Annotation; 20 | 21 | /** 22 | * Interface for Annotation Processors responsible for dealing with annotated method parameters. 23 | * Used in both reflective and compile time modes. 24 | * 25 | * @param supported annotation. 26 | */ 27 | public interface ParameterAnnotationProcessor { 28 | 29 | /** 30 | * Evaluate the annotation against the provided parameter information. Implementations are 31 | * expected to update the builder via side-effects. 32 | * 33 | * @param annotation to evaluate. 34 | * @param name of the parameter. 35 | * @param index of the parameter in the method. 36 | * @param type fully qualified class name of the parameter type. 37 | * @param builder with the current method context. 38 | */ 39 | void process(T annotation, String name, Integer index, String type, 40 | TargetMethodDefinition.Builder builder); 41 | } 42 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/type/GenericArrayTypeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl.type; 18 | 19 | import java.lang.reflect.GenericArrayType; 20 | import java.lang.reflect.Type; 21 | 22 | /** 23 | * Type Definition for a {@link GenericArrayType}, a ParameterizedType with an Array supplied 24 | * as the Type Variable. 25 | */ 26 | public class GenericArrayTypeDefinition extends AbstractTypeDefinition implements GenericArrayType { 27 | 28 | private final TypeDefinition genericComponentType; 29 | 30 | /** 31 | * Creates a new {@link GenericArrayTypeDefinition}. 32 | * 33 | * @param genericComponentType containing the Type to define. 34 | */ 35 | GenericArrayTypeDefinition(TypeDefinition genericComponentType) { 36 | this.genericComponentType = genericComponentType; 37 | } 38 | 39 | @Override 40 | public Class getType() { 41 | return this.genericComponentType.getType(); 42 | } 43 | 44 | @Override 45 | public Type getGenericComponentType() { 46 | return this.genericComponentType; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/type/AbstractTypeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl.type; 18 | 19 | import java.lang.reflect.Type; 20 | import java.util.Collection; 21 | import java.util.concurrent.Future; 22 | 23 | /** 24 | * Basic Type Definition. 25 | */ 26 | public abstract class AbstractTypeDefinition implements TypeDefinition, Type { 27 | 28 | /** 29 | * Returns the type defined. 30 | * 31 | * @return defined type. 32 | */ 33 | @Override 34 | public Class getActualType() { 35 | return this.getType(); 36 | } 37 | 38 | /** 39 | * {@inheritDoc} 40 | */ 41 | @Override 42 | public boolean isCollectionLike() { 43 | Class type = this.getType(); 44 | return type.isArray() 45 | || Iterable.class.isAssignableFrom(type) 46 | || Collection.class.isAssignableFrom(type); 47 | } 48 | 49 | /** 50 | * {@inheritDoc} 51 | */ 52 | @Override 53 | public boolean isContainer() { 54 | Class type = this.getType(); 55 | return Future.class.isAssignableFrom(type); 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/impl/HeadersAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract.impl; 18 | 19 | import feign.contract.AnnotationProcessor; 20 | import feign.contract.Header; 21 | import feign.contract.Headers; 22 | import feign.contract.TargetMethodDefinition.Builder; 23 | 24 | /** 25 | * Annotation Processor for the {@link Headers} annotation. 26 | */ 27 | public class HeadersAnnotationProcessor implements AnnotationProcessor { 28 | 29 | private final HeaderAnnotationProcessor headerAnnotationProcessor; 30 | 31 | /** 32 | * Creates a new {@link HeadersAnnotationProcessor}. 33 | */ 34 | public HeadersAnnotationProcessor() { 35 | this.headerAnnotationProcessor = new HeaderAnnotationProcessor(); 36 | } 37 | 38 | @Override 39 | public void process(Headers annotation, Builder builder) { 40 | Header[] headers = annotation.value(); 41 | if (headers.length != 0) { 42 | for (Header value : headers) { 43 | this.headerAnnotationProcessor.process(value, builder); 44 | } 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /core/src/test/java/feign/exception/RethrowExceptionHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.exception; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import feign.ExceptionHandler; 22 | import feign.ExceptionHandler.RethrowExceptionHandler; 23 | import java.io.IOException; 24 | import org.junit.jupiter.api.Test; 25 | 26 | class RethrowExceptionHandlerTest { 27 | 28 | @Test 29 | void exceptions_shouldBeThrown() { 30 | ExceptionHandler exceptionHandler = new RethrowExceptionHandler(); 31 | RuntimeException exception = exceptionHandler.apply(new FeignException("Test", "method")); 32 | assertThat(exception).isInstanceOf(FeignException.class); 33 | } 34 | 35 | @Test 36 | void exceptions_shouldBeWrapped() { 37 | ExceptionHandler exceptionHandler = new RethrowExceptionHandler(); 38 | RuntimeException exception = exceptionHandler.apply(new IOException("bad")); 39 | assertThat(exception).isInstanceOf(RuntimeException.class); 40 | assertThat(exception.getCause()).isInstanceOf(IOException.class); 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /core/src/test/java/feign/encoder/StringEncoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.encoder; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import static org.mockito.Mockito.mock; 21 | 22 | import feign.RequestEncoder; 23 | import feign.RequestEntity; 24 | import feign.http.RequestSpecification; 25 | import org.junit.jupiter.api.Test; 26 | 27 | class StringEncoderTest { 28 | 29 | @Test 30 | void apply_toString() { 31 | RequestEncoder encoder = new StringEncoder(); 32 | RequestSpecification requestSpecification = mock(RequestSpecification.class); 33 | RequestEntity entity = encoder.apply("content", requestSpecification); 34 | assertThat(entity).isNotNull(); 35 | assertThat(entity.getData()).isNotEmpty(); 36 | } 37 | 38 | @Test 39 | void apply_withNullSkips() { 40 | RequestEncoder encoder = new StringEncoder(); 41 | RequestSpecification requestSpecification = mock(RequestSpecification.class); 42 | RequestEntity entity = encoder.apply(null, requestSpecification); 43 | assertThat(entity).isNull(); 44 | } 45 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/retry/ExceptionCondition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | /** 20 | * Retry Condition that determines if a retry should occur based on the last exception provided. 21 | */ 22 | public class ExceptionCondition implements RetryCondition { 23 | 24 | private final Class exception; 25 | 26 | /** 27 | * Creates a new {@link ExceptionCondition}. 28 | * 29 | * @param exception that should trigger a retry. 30 | */ 31 | public ExceptionCondition(Class exception) { 32 | this.exception = exception; 33 | } 34 | 35 | /** 36 | * Determines if the last exception provided should trigger a retry. 37 | * 38 | * @param retryContext to test 39 | * @return {@literal true} if the last exception should trigger, {@literal false} otherwise. 40 | */ 41 | @Override 42 | public boolean test(RetryContext retryContext) { 43 | return retryContext.getLastException() 44 | .map(throwable -> exception.isAssignableFrom(throwable.getClass())) 45 | .orElse(false); 46 | 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/feign/exception/FeignException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.exception; 18 | 19 | /** 20 | * Base Exception for all internal errors. 21 | */ 22 | public class FeignException extends RuntimeException { 23 | 24 | private String method; 25 | 26 | /** 27 | * Creates a new Feign Exception. 28 | * 29 | * @param message for the exception. 30 | * @param method name in which the exception occurred. 31 | */ 32 | public FeignException(String message, String method) { 33 | super(message); 34 | this.method = method; 35 | } 36 | 37 | /** 38 | * Creates a new Feign Exception. 39 | * 40 | * @param message for the exception. 41 | * @param cause of the exception. 42 | * @param method name in which the exception occurred. 43 | */ 44 | public FeignException(String message, Throwable cause, String method) { 45 | super(message, cause); 46 | this.method = method; 47 | } 48 | 49 | /** 50 | * Method name. 51 | * 52 | * @return the method name. 53 | */ 54 | public String getMethod() { 55 | return method; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /core/src/test/java/feign/template/tck/TestCase.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.tck; 18 | 19 | import com.fasterxml.jackson.annotation.JsonCreator; 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | 23 | public class TestCase { 24 | 25 | private String expression; 26 | private List results = new ArrayList<>(); 27 | 28 | protected TestCase() { 29 | super(); 30 | } 31 | 32 | @SuppressWarnings("unchecked") 33 | @JsonCreator 34 | protected TestCase(List values) { 35 | this.expression = (String) values.get(0); 36 | 37 | if (List.class.isAssignableFrom(values.get(1).getClass())) { 38 | this.results.addAll((List) values.get(1)); 39 | } else if (Boolean.class.isAssignableFrom(values.get(1).getClass())) { 40 | this.results.add(((Boolean) values.get(1)).toString()); 41 | } else { 42 | 43 | this.results.add((String) values.get(1)); 44 | } 45 | } 46 | 47 | public String getExpression() { 48 | return expression; 49 | } 50 | 51 | 52 | public List getResults() { 53 | return results; 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/AbsoluteUriTarget.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl; 18 | 19 | import feign.http.RequestSpecification; 20 | import java.net.URI; 21 | import java.util.function.Consumer; 22 | 23 | /** 24 | * Consumer for handling absolute base URI targets. 25 | */ 26 | public class AbsoluteUriTarget implements Consumer { 27 | 28 | private final URI uri; 29 | 30 | public AbsoluteUriTarget(String uri) { 31 | this.uri = URI.create(uri); 32 | } 33 | 34 | public AbsoluteUriTarget(URI uri) { 35 | this.uri = uri; 36 | } 37 | 38 | @Override 39 | public void accept(RequestSpecification specification) { 40 | specification.uri(specification.uri() 41 | .map(current -> { 42 | /* the current uri must be relative to use this target */ 43 | if (current.isAbsolute()) { 44 | throw new IllegalStateException("URIs must be relative when using a UriTarget."); 45 | } 46 | 47 | /* prepend this uri to the current uri */ 48 | return URI.create(uri.toString() + current); 49 | }).orElse(uri)); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /core/src/test/java/feign/encoder/StringRequestEntityTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.encoder; 18 | 19 | 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | 22 | import java.nio.charset.StandardCharsets; 23 | import org.junit.jupiter.api.Test; 24 | 25 | /** 26 | * Unit Test for StringRequestEntity. 27 | */ 28 | class StringRequestEntityTest { 29 | 30 | @Test 31 | void canCreate() { 32 | final String content = "content"; 33 | final int contentLength = content.getBytes(StandardCharsets.UTF_8).length; 34 | 35 | StringRequestEntity requestEntity = new StringRequestEntity(content); 36 | assertThat(requestEntity).isNotNull(); 37 | assertThat(requestEntity.getCharset()).isPresent() 38 | .hasValue(StandardCharsets.UTF_8); 39 | assertThat(requestEntity.getContentType()).isNotEmpty() 40 | .isEqualToIgnoringCase(StringRequestEntity.TEXT_PLAIN); 41 | assertThat(requestEntity.getData()).isNotEmpty() 42 | .hasSize(contentLength); 43 | assertThat(requestEntity.getContentLength()).isEqualTo(contentLength); 44 | } 45 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/retry/RetryContext.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | import feign.Response; 20 | import java.util.Optional; 21 | 22 | /** 23 | * The state of the current Retry attempt. 24 | */ 25 | public final class RetryContext { 26 | 27 | private final int attempts; 28 | private final Throwable lastException; 29 | private final Response response; 30 | 31 | /** 32 | * Creates a new {@link RetryContext}. 33 | * 34 | * @param attempts made. 35 | * @param lastException that occurred, if any. 36 | * @param response that was returned, if any. 37 | */ 38 | public RetryContext(int attempts, Throwable lastException, Response response) { 39 | this.attempts = attempts; 40 | this.lastException = lastException; 41 | this.response = response; 42 | } 43 | 44 | public int getAttempts() { 45 | return this.attempts; 46 | } 47 | 48 | public Optional getLastException() { 49 | return Optional.ofNullable(this.lastException); 50 | } 51 | 52 | public Optional getResponse() { 53 | return Optional.ofNullable(this.response); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /core/src/test/java/feign/http/HttpExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.http; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import java.io.IOException; 22 | import org.junit.jupiter.api.Test; 23 | 24 | class HttpExceptionTest { 25 | 26 | @Test 27 | void canCreate() { 28 | HttpException exception = new HttpException("Request Failed", new IOException()); 29 | assertThat(exception).isNotNull(); 30 | } 31 | 32 | @Test 33 | void canCreate_withRequest() { 34 | HttpException exception = 35 | new HttpException("Request Failed", new IOException(), 36 | new HttpRequest()); 37 | assertThat(exception.getRequest()).isNotEmpty(); 38 | assertThat(exception.getResponse()).isEmpty(); 39 | } 40 | 41 | @Test 42 | void canCreate_withRequestAndResponse() { 43 | HttpException exception = 44 | new HttpException("Request Failed", new IOException(), 45 | new HttpRequest(), 46 | new HttpResponse()); 47 | assertThat(exception.getRequest()).isNotEmpty(); 48 | assertThat(exception.getResponse()).isNotEmpty(); 49 | } 50 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/retry/BackoffRetryInterval.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | /** 20 | * Retry interval that uses an exponentially increasing interval. 21 | */ 22 | public class BackoffRetryInterval implements RetryInterval { 23 | 24 | private final long interval; 25 | private long multiplier; 26 | private long maxInterval; 27 | 28 | /** 29 | * Creates a new {@link BackoffRetryInterval}. 30 | * 31 | * @param interval to start with. 32 | * @param multiplier to multiply the interval with. 33 | * @param maxInterval to use. 34 | */ 35 | public BackoffRetryInterval(long interval, long multiplier, long maxInterval) { 36 | this.interval = interval; 37 | this.multiplier = multiplier; 38 | this.maxInterval = maxInterval; 39 | } 40 | 41 | @Override 42 | public long getInterval(RetryContext retryContext) { 43 | if (retryContext.getAttempts() == 1) { 44 | return this.interval; 45 | } 46 | 47 | long interval = (retryContext.getAttempts() - 1) * (this.interval * this.multiplier); 48 | return (interval < this.maxInterval) ? interval : this.maxInterval; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/test/java/feign/support/AuditingExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.support; 18 | 19 | import java.util.HashSet; 20 | import java.util.Set; 21 | import java.util.concurrent.Executor; 22 | import java.util.concurrent.atomic.AtomicInteger; 23 | 24 | public class AuditingExecutor implements Executor { 25 | 26 | private Set threads = new HashSet<>(); 27 | private AtomicInteger executionCount = new AtomicInteger(); 28 | private Executor delegate; 29 | 30 | public AuditingExecutor() { 31 | super(); 32 | } 33 | 34 | public AuditingExecutor(Executor delegate) { 35 | this.delegate = delegate; 36 | } 37 | 38 | @Override 39 | public void execute(Runnable command) { 40 | executionCount.incrementAndGet(); 41 | this.threads.add(Thread.currentThread().getId()); 42 | 43 | /* run on the current thread */ 44 | if (delegate != null) { 45 | delegate.execute(command); 46 | } else { 47 | command.run(); 48 | } 49 | } 50 | 51 | public Set getThreads() { 52 | return this.threads; 53 | } 54 | 55 | public int getExecutionCount() { 56 | return this.executionCount.intValue(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /core/src/main/java/feign/retry/StatusCodeCondition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | import feign.support.Assert; 20 | 21 | /** 22 | * Retry Condition that determines if the status code on the Response should trigger a retry. 23 | */ 24 | public class StatusCodeCondition implements RetryCondition { 25 | 26 | private final Integer statusCode; 27 | 28 | /** 29 | * Creates a new {@link StatusCodeCondition}. 30 | * 31 | * @param statusCode that should trigger a retry. 32 | */ 33 | public StatusCodeCondition(Integer statusCode) { 34 | Assert.isNotNull(statusCode, "statusCode must not be null."); 35 | this.statusCode = statusCode; 36 | } 37 | 38 | /** 39 | * Determines if the status code on the most recent Response should trigger a retry. 40 | * 41 | * @param retryContext to test. 42 | * @return {@literal true} if the status code should trigger a retry, {@literal false} otherwise. 43 | */ 44 | @Override 45 | public boolean test(final RetryContext retryContext) { 46 | return retryContext.getResponse() 47 | .map(response -> statusCode.equals(response.status())) 48 | .orElse(false); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/impl/ParamAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract.impl; 18 | 19 | import feign.contract.Param; 20 | import feign.contract.ParameterAnnotationProcessor; 21 | import feign.contract.TargetMethodDefinition.Builder; 22 | import feign.contract.TargetMethodParameterDefinition; 23 | import feign.template.ExpressionExpander; 24 | 25 | /** 26 | * Annotation Processor for the {@link Param} annotation. Registers the parameter, along with 27 | * it's name, type and index in the method, with the method definition. 28 | */ 29 | public class ParamAnnotationProcessor implements ParameterAnnotationProcessor { 30 | 31 | @Override 32 | public void process(Param annotation, String name, Integer index, String type, Builder builder) { 33 | Class expanderClass = annotation.expander(); 34 | String expanderClassName = expanderClass.getName(); 35 | 36 | builder.parameterDefinition( 37 | index, TargetMethodParameterDefinition.builder() 38 | .name(annotation.value()) 39 | .index(index) 40 | .type(type) 41 | .expanderClassName(expanderClassName) 42 | .build()); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /core/src/main/java/feign/Request.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.http.HttpMethod; 20 | import java.net.URI; 21 | import java.util.Collection; 22 | 23 | /** 24 | * Information about a Request to be made. 25 | */ 26 | public interface Request { 27 | 28 | /** 29 | * URI of the Target. 30 | * 31 | * @return request uri. 32 | */ 33 | URI uri(); 34 | 35 | /** 36 | * Content to be sent with the request. 37 | * 38 | * @return request content. 39 | */ 40 | byte[] content(); 41 | 42 | /** 43 | * Length of the Content to be sent. 44 | * 45 | * @return request content size. 46 | */ 47 | int contentLength(); 48 | 49 | /** 50 | * MIME Type or other equivalent content type. May be {@code null} 51 | * 52 | * @return request content type. 53 | */ 54 | String contentType(); 55 | 56 | /** 57 | * Http Method for this request. 58 | * 59 | * @return http method. 60 | */ 61 | HttpMethod method(); 62 | 63 | /** 64 | * Headers for the request. 65 | * 66 | * @return request headers. 67 | */ 68 | Collection
headers(); 69 | 70 | /** 71 | * Options for this request. 72 | * 73 | * @return request options. 74 | */ 75 | RequestOptions options(); 76 | } 77 | -------------------------------------------------------------------------------- /core/src/main/java/feign/encoder/StringRequestEntity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.encoder; 18 | 19 | import feign.RequestEntity; 20 | import java.nio.charset.Charset; 21 | import java.nio.charset.StandardCharsets; 22 | import java.util.Arrays; 23 | import java.util.Optional; 24 | 25 | /** 26 | * Simple Request Entity backed by a String. 27 | */ 28 | public class StringRequestEntity implements RequestEntity { 29 | 30 | public static final String TEXT_PLAIN = "text/plain"; 31 | private final byte[] data; 32 | private final int length; 33 | private final Charset charset = StandardCharsets.UTF_8; 34 | 35 | public StringRequestEntity(String content) { 36 | this.data = content.getBytes(StandardCharsets.UTF_8); 37 | this.length = data.length; 38 | } 39 | 40 | @Override 41 | public Optional getCharset() { 42 | return Optional.of(this.charset); 43 | } 44 | 45 | @Override 46 | public int getContentLength() { 47 | return this.length; 48 | } 49 | 50 | @Override 51 | public String getContentType() { 52 | return TEXT_PLAIN; 53 | } 54 | 55 | @Override 56 | public byte[] getData() { 57 | return Arrays.copyOf(this.data, this.data.length); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /core/src/test/java/feign/retry/BackoffRetryIntervalTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class BackoffRetryIntervalTest { 24 | 25 | @Test 26 | void getInterval_shouldApplyMultiplier() { 27 | BackoffRetryInterval interval = 28 | new BackoffRetryInterval(1000, 2, 10000); 29 | 30 | /* should be 1000, 2000, 4000 */ 31 | assertThat(interval.getInterval(new RetryContext(1, null, null))) 32 | .isEqualTo(1000); 33 | assertThat(interval.getInterval(new RetryContext(2, null, null))) 34 | .isEqualTo(2000); 35 | assertThat(interval.getInterval(new RetryContext(3, null, null))) 36 | .isEqualTo(4000); 37 | } 38 | 39 | @Test 40 | void getInterval_shouldNotExceedMax() { 41 | BackoffRetryInterval interval = 42 | new BackoffRetryInterval(1000, 2, 10000); 43 | 44 | /* should be 8, 10, and 10 */ 45 | assertThat(interval.getInterval(new RetryContext(5, null, null))) 46 | .isEqualTo(8000); 47 | assertThat(interval.getInterval(new RetryContext(6, null, null))) 48 | .isEqualTo(10000); 49 | assertThat(interval.getInterval(new RetryContext(7, null, null))) 50 | .isEqualTo(10000); 51 | } 52 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/Response.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.util.List; 22 | 23 | /** 24 | * The Response model. 25 | */ 26 | public interface Response extends AutoCloseable { 27 | 28 | /** 29 | * Status Code. 30 | * 31 | * @return status code. 32 | */ 33 | int status(); 34 | 35 | /** 36 | * Status Reason. 37 | * 38 | * @return status reason. 39 | */ 40 | String reason(); 41 | 42 | /** 43 | * List of Headers provided in the Response. 44 | * 45 | * @return response headers. 46 | */ 47 | List
headers(); 48 | 49 | /** 50 | * The length of the Response. 51 | * 52 | * @return content length. 53 | */ 54 | int contentLength(); 55 | 56 | /** 57 | * An Input Stream backed by the Response data. It is the responsibility of the caller 58 | * to close this stream. 59 | * 60 | * @return a response data backed Input Stream. 61 | */ 62 | InputStream body(); 63 | 64 | /** 65 | * Reads the entire response and returns the data as a byte array. 66 | * 67 | * @return the Response data as a byte array. 68 | * @throws IOException if the response could not be read. 69 | */ 70 | byte[] toByteArray() throws IOException; 71 | 72 | 73 | } 74 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/impl/RequestAnnotationProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract.impl; 18 | 19 | import feign.contract.AnnotationProcessor; 20 | import feign.contract.Request; 21 | import feign.contract.TargetMethodDefinition.Builder; 22 | import feign.http.HttpMethod; 23 | import feign.support.StringUtils; 24 | 25 | /** 26 | * Annotation processor for the {@link Request} annotation. Responsible for parsing the top level 27 | * request information. 28 | */ 29 | public class RequestAnnotationProcessor implements AnnotationProcessor { 30 | 31 | /** 32 | * Read the uri, request method, and other request specific configuration. 33 | * 34 | * @param annotation to evaluate. 35 | * @param builder with the current method context. 36 | */ 37 | @Override 38 | public void process(Request annotation, Builder builder) { 39 | String uri = (StringUtils.isNotEmpty(annotation.uri())) ? annotation.uri() : annotation.value(); 40 | HttpMethod httpMethod = annotation.method(); 41 | boolean followRedirects = annotation.followRedirects(); 42 | 43 | builder.uri(uri) 44 | .method(httpMethod) 45 | .followRedirects(followRedirects) 46 | .connectTimeout(annotation.connectTimeout()) 47 | .readTimeout(annotation.readTimeout()); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /core/src/main/java/feign/proxy/ProxyFeign.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.proxy; 18 | 19 | import feign.Contract; 20 | import feign.Feign; 21 | import feign.FeignConfiguration; 22 | import feign.contract.TargetDefinition; 23 | import java.lang.reflect.Proxy; 24 | 25 | /** 26 | * Feign implementation that creates a JDK Proxy for the {@link feign.Target} instance. 27 | */ 28 | public class ProxyFeign extends Feign { 29 | 30 | /** 31 | * Creates a JDK Proxy for the {@link feign.Target} provided in the configuration. 32 | * 33 | * @param configuration for this instance. 34 | * @param of the {@link feign.Target} class to proxy. 35 | * @return a JDK Proxy instance. 36 | */ 37 | @SuppressWarnings("unchecked") 38 | @Override 39 | protected T create(Class targetType, FeignConfiguration configuration) { 40 | /* apply the contract to the target */ 41 | Contract contract = configuration.getContract(); 42 | TargetDefinition definition = contract.apply(targetType, configuration); 43 | 44 | /* create the provided target in a proxy */ 45 | ProxyTarget proxyTarget = new ProxyTarget<>(definition, configuration); 46 | 47 | /* create a new JDK Proxy for the Target */ 48 | return (T) Proxy.newProxyInstance( 49 | proxyTarget.type().getClassLoader(), 50 | new Class[] {proxyTarget.type()}, 51 | proxyTarget); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /tests/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 22 | 23 | feignx 24 | io.github.openfeign.incubating 25 | 0.0.1-SNAPSHOT 26 | 27 | 4.0.0 28 | 29 | feign-tests 30 | 0.0.1-SNAPSHOT 31 | 32 | 33 | ${project.basedir}/.. 34 | 35 | 36 | 37 | 38 | io.github.openfeign.incubating 39 | feignx-core 40 | ${project.version} 41 | 42 | 43 | 44 | 45 | 46 | org.jacoco 47 | jacoco-maven-plugin 48 | 49 | 50 | aggregate 51 | test 52 | 53 | report-aggregate 54 | 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /core/src/test/java/feign/retry/RetryConditionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import org.junit.jupiter.api.Test; 22 | 23 | class RetryConditionTest { 24 | 25 | private final RetryCondition alwaysTrue = context -> true; 26 | private final RetryCondition alwaysFalse = context -> false; 27 | 28 | @Test 29 | void or() { 30 | RetryContext context = new RetryContext(1, null, null); 31 | RetryCondition trueCondition = alwaysTrue.or(alwaysFalse); 32 | RetryCondition falseCondition = alwaysFalse.or(alwaysFalse); 33 | 34 | /* logical or */ 35 | assertThat(trueCondition.test(context)).isTrue(); 36 | assertThat(falseCondition.test(context)).isFalse(); 37 | } 38 | 39 | @Test 40 | void and() { 41 | RetryContext context = new RetryContext(1, null, null); 42 | RetryCondition falseCondition = alwaysTrue.and(alwaysFalse); 43 | RetryCondition trueCondition = alwaysTrue.and(alwaysTrue); 44 | 45 | /* logical and */ 46 | assertThat(falseCondition.test(context)).isFalse(); 47 | assertThat(trueCondition.test(context)).isTrue(); 48 | } 49 | 50 | @Test 51 | void negate() { 52 | RetryContext context = new RetryContext(1, null, null); 53 | RetryCondition negative = alwaysTrue.not(); 54 | 55 | assertThat(negative.test(context)).isFalse(); 56 | assertThat(alwaysFalse.not().test(context)).isTrue(); 57 | } 58 | 59 | } -------------------------------------------------------------------------------- /core/src/test/java/feign/assertions/HttpHeaderAssert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.assertions; 18 | 19 | import feign.http.HttpHeader; 20 | 21 | /** 22 | * {@link HttpHeader} specific assertions - Generated by CustomAssertionGenerator. 23 | * 24 | * Although this class is not final to allow Soft assertions proxy, if you wish to extend it, 25 | * extend {@link AbstractHttpHeaderAssert} instead. 26 | */ 27 | @javax.annotation.Generated(value="assertj-assertions-generator") 28 | public class HttpHeaderAssert extends AbstractHttpHeaderAssert { 29 | 30 | /** 31 | * Creates a new {@link HttpHeaderAssert} to make assertions on actual HttpHeader. 32 | * @param actual the HttpHeader we want to make assertions on. 33 | */ 34 | public HttpHeaderAssert(HttpHeader actual) { 35 | super(actual, HttpHeaderAssert.class); 36 | } 37 | 38 | /** 39 | * An entry point for HttpHeaderAssert to follow AssertJ standard assertThat() statements.
40 | * With a static import, one can write directly: assertThat(myHttpHeader) and get specific assertion with code completion. 41 | * @param actual the HttpHeader we want to make assertions on. 42 | * @return a new {@link HttpHeaderAssert} 43 | */ 44 | @org.assertj.core.util.CheckReturnValue 45 | public static HttpHeaderAssert assertThat(HttpHeader actual) { 46 | return new HttpHeaderAssert(actual); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/AsyncTargetMethodHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl; 18 | 19 | import feign.FeignConfiguration; 20 | import feign.contract.TargetMethodDefinition; 21 | import java.util.concurrent.CompletableFuture; 22 | import java.util.concurrent.Future; 23 | 24 | /** 25 | * Method Handler that deals with asynchronous return types, such as {@link Future}. 26 | */ 27 | public class AsyncTargetMethodHandler extends AbstractTargetMethodHandler { 28 | 29 | /** 30 | * Creates a new Abstract Target HttpMethod Handler. 31 | * 32 | * @param targetMethodDefinition containing the method configuration. 33 | * @param configuration with the target configuration. 34 | */ 35 | AsyncTargetMethodHandler(TargetMethodDefinition targetMethodDefinition, 36 | FeignConfiguration configuration) { 37 | super(targetMethodDefinition, configuration); 38 | } 39 | 40 | /** 41 | * Handles the results of the Request execution by wrapping the result in a new {@link 42 | * CompletableFuture} containing the decoded Response body. The method handler's Executor is used 43 | * for this future. 44 | * 45 | * @param result Future containing the results of the request. 46 | * @return a {@link CompletableFuture} reference wrapping the results. 47 | */ 48 | @Override 49 | protected Object handleResponse(CompletableFuture result) { 50 | /* let the caller decide what to do with it */ 51 | return result; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /core/src/test/java/feign/assertions/HttpRequestAssert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.assertions; 18 | 19 | import feign.http.HttpRequest; 20 | 21 | /** 22 | * {@link HttpRequest} specific assertions - Generated by CustomAssertionGenerator. 23 | * 24 | * Although this class is not final to allow Soft assertions proxy, if you wish to extend it, 25 | * extend {@link AbstractHttpRequestAssert} instead. 26 | */ 27 | @javax.annotation.Generated(value="assertj-assertions-generator") 28 | public class HttpRequestAssert extends AbstractHttpRequestAssert { 29 | 30 | /** 31 | * Creates a new {@link HttpRequestAssert} to make assertions on actual HttpRequest. 32 | * @param actual the HttpRequest we want to make assertions on. 33 | */ 34 | public HttpRequestAssert(HttpRequest actual) { 35 | super(actual, HttpRequestAssert.class); 36 | } 37 | 38 | /** 39 | * An entry point for HttpRequestAssert to follow AssertJ standard assertThat() statements.
40 | * With a static import, one can write directly: assertThat(myHttpRequest) and get specific assertion with code completion. 41 | * @param actual the HttpRequest we want to make assertions on. 42 | * @return a new {@link HttpRequestAssert} 43 | */ 44 | @org.assertj.core.util.CheckReturnValue 45 | public static HttpRequestAssert assertThat(HttpRequest actual) { 46 | return new HttpRequestAssert(actual); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/test/java/feign/assertions/HttpResponseAssert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.assertions; 18 | 19 | import feign.http.HttpResponse; 20 | 21 | /** 22 | * {@link HttpResponse} specific assertions - Generated by CustomAssertionGenerator. 23 | * 24 | * Although this class is not final to allow Soft assertions proxy, if you wish to extend it, 25 | * extend {@link AbstractHttpResponseAssert} instead. 26 | */ 27 | @javax.annotation.Generated(value="assertj-assertions-generator") 28 | public class HttpResponseAssert extends AbstractHttpResponseAssert { 29 | 30 | /** 31 | * Creates a new {@link HttpResponseAssert} to make assertions on actual HttpResponse. 32 | * @param actual the HttpResponse we want to make assertions on. 33 | */ 34 | public HttpResponseAssert(HttpResponse actual) { 35 | super(actual, HttpResponseAssert.class); 36 | } 37 | 38 | /** 39 | * An entry point for HttpResponseAssert to follow AssertJ standard assertThat() statements.
40 | * With a static import, one can write directly: assertThat(myHttpResponse) and get specific assertion with code completion. 41 | * @param actual the HttpResponse we want to make assertions on. 42 | * @return a new {@link HttpResponseAssert} 43 | */ 44 | @org.assertj.core.util.CheckReturnValue 45 | public static HttpResponseAssert assertThat(HttpResponse actual) { 46 | return new HttpResponseAssert(actual); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /core/src/main/java/feign/support/StringUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.support; 18 | 19 | /** 20 | * String Utility Methods. 21 | */ 22 | public class StringUtils { 23 | 24 | public static final String EMPTY = ""; 25 | 26 | /** 27 | * Determines if the provided String is not empty or {@literal null}. 28 | * 29 | * @param value to evaluate. 30 | * @return {@literal true} if the value is not empty or {@literal null}, {@literal false} 31 | * otherwise. 32 | */ 33 | public static boolean isNotEmpty(String value) { 34 | return value != null && !value.isEmpty(); 35 | } 36 | 37 | /** 38 | * Determines if the provided String is empty or {@literal null}. 39 | * 40 | * @param value to evaluate. 41 | * @return {@literal true} if the value is empty or {@literal null}, {@literal false} otherwise. 42 | */ 43 | public static boolean isEmpty(String value) { 44 | return value == null || value.isEmpty(); 45 | } 46 | 47 | /** 48 | * Determines if the provided String is only numbers. 49 | * 50 | * @param value to evaluate. 51 | * @return {@literal true} if the string contains only numbers, {@literal false} otherwise. 52 | */ 53 | public static boolean isNumeric(String value) { 54 | if (isNotEmpty(value)) { 55 | int length = value.length(); 56 | for (int i = 0; i < length; i++) { 57 | if (!Character.isDigit(value.charAt(i))) { 58 | return false; 59 | } 60 | } 61 | return true; 62 | } 63 | return false; 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /core/src/main/java/feign/retry/RetryCondition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.retry; 18 | 19 | /** 20 | * Condition Predicate that determines if the request should be retried. 21 | */ 22 | public interface RetryCondition { 23 | 24 | /** 25 | * Evaluate the context against this condition. 26 | * 27 | * @param context to evaluate. 28 | * @return {@literal true} if the condition is met, {@literal false} otherwise. 29 | */ 30 | boolean test(RetryContext context); 31 | 32 | /** 33 | * Returns a composed {@link RetryCondition} logically OR between this {@link RetryCondition} and 34 | * the supplied condition. 35 | * 36 | * @param condition to be OR'd with. 37 | * @return the composed condition. 38 | */ 39 | default RetryCondition or(final RetryCondition condition) { 40 | return (context -> test(context) || condition.test(context)); 41 | } 42 | 43 | /** 44 | * Returns a composed {@link RetryCondition} logically AND between this {@link RetryCondition} and 45 | * the supplied condition. 46 | * 47 | * @param condition to be AND'd with. 48 | * @return the composed condition. 49 | */ 50 | default RetryCondition and(final RetryCondition condition) { 51 | return (context -> test(context) && condition.test(context)); 52 | } 53 | 54 | /** 55 | * Returns a {@link RetryCondition} where the original condition is inverted. 56 | * 57 | * @return the inverted condition. 58 | */ 59 | default RetryCondition not() { 60 | return (context -> !test(context)); 61 | } 62 | } 63 | 64 | 65 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/type/TypeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl.type; 18 | 19 | import java.lang.reflect.Type; 20 | 21 | /** 22 | * Generic Type Definition. 23 | */ 24 | public interface TypeDefinition extends Type { 25 | 26 | /** 27 | * The Class type defining this Type. 28 | * 29 | * @return class reference. 30 | */ 31 | Class getType(); 32 | 33 | /** 34 | * Return the actual type for this definition. For basic types, this will be the same as 35 | * {@link #getType()}, but for parameterized types that are collections or containers, this 36 | * will return the type contained. 37 | * 38 | * @return the actual type reference. 39 | */ 40 | Class getActualType(); 41 | 42 | /** 43 | * Returns whether this definition is considered a Collection, which is basically anything 44 | * that extends {@link Iterable}. The expectation here is that, if a type is collection like, 45 | * then {@link #getType()} will return an {@link Iterable} type. 46 | * 47 | * @return if this definition is for a Collection. 48 | */ 49 | boolean isCollectionLike(); 50 | 51 | /** 52 | * Returns whether this definition is one of a select type of objects that act as containers. 53 | * Container types act as vehicles to decorate the actual types. The expectation here is that 54 | * {@link #getActualType()} will return the contained type. Some examples are 55 | * {@link java.util.Optional} and {@link java.util.concurrent.Future}. 56 | * 57 | * @return if this definition represents a contained type. 58 | */ 59 | boolean isContainer(); 60 | 61 | } 62 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/type/WildCardTypeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl.type; 18 | 19 | import java.lang.reflect.Type; 20 | import java.lang.reflect.WildcardType; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Type Definition for a Parameterized Type where one of the Type Variable's defined is a {@code ?} 26 | * wildcard. 27 | */ 28 | public class WildCardTypeDefinition extends AbstractTypeDefinition implements WildcardType { 29 | 30 | private List upperBounds; 31 | private List lowerBounds; 32 | 33 | /** 34 | * Creates a new WildcardType definition. 35 | */ 36 | WildCardTypeDefinition() { 37 | super(); 38 | this.upperBounds = new ArrayList<>(); 39 | this.lowerBounds = new ArrayList<>(); 40 | } 41 | 42 | @Override 43 | public Class getType() { 44 | /* only one upper or lower bound is allowed, so first default to the lower bound */ 45 | if (!this.lowerBounds.isEmpty()) { 46 | return this.lowerBounds.get(0).getType(); 47 | } else if (!this.upperBounds.isEmpty()) { 48 | return this.upperBounds.get(0).getType(); 49 | } 50 | return null; 51 | } 52 | 53 | @Override 54 | public Type[] getUpperBounds() { 55 | return this.upperBounds.toArray(new Type[]{}); 56 | } 57 | 58 | @Override 59 | public Type[] getLowerBounds() { 60 | return this.lowerBounds.toArray(new Type[]{}); 61 | } 62 | 63 | void addUpperBound(TypeDefinition typeDefinition) { 64 | this.upperBounds.add(typeDefinition); 65 | } 66 | 67 | void addLowerBound(TypeDefinition typeDefinition) { 68 | this.lowerBounds.add(typeDefinition); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /core/src/test/java/feign/template/expander/ExpanderUtilsTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.expander; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import feign.contract.Param; 22 | import java.util.Map; 23 | import org.junit.jupiter.api.Test; 24 | 25 | class ExpanderUtilsTest { 26 | 27 | @Test 28 | void enum_isSimple() { 29 | Constants constants = Constants.ONE; 30 | assertThat(ExpanderUtils.isSimpleType(constants.getClass())).isTrue(); 31 | } 32 | 33 | @Test 34 | void array_isSimple() { 35 | int[] numbers = new int[10]; 36 | assertThat(ExpanderUtils.isSimpleType(numbers.getClass())).isTrue(); 37 | } 38 | 39 | @Test 40 | void annotation_isSimple() { 41 | assertThat(ExpanderUtils.isSimpleType(Param.class)).isTrue(); 42 | } 43 | 44 | @Test 45 | void primitive_isSimple() { 46 | assertThat(ExpanderUtils.isSimpleType(int.class)).isTrue(); 47 | } 48 | 49 | @Test 50 | void number_isNotSimple() { 51 | assertThat(ExpanderUtils.isSimpleType(Integer.class)).isFalse(); 52 | } 53 | 54 | @Test 55 | void string_isSimple() { 56 | assertThat(ExpanderUtils.isSimpleType(String.class)).isTrue(); 57 | } 58 | 59 | @Test 60 | void iterable_isSimple() { 61 | assertThat(ExpanderUtils.isSimpleType(Iterable.class)).isTrue(); 62 | } 63 | 64 | @Test 65 | void map_isSimple() { 66 | assertThat(ExpanderUtils.isSimpleType(Map.class)).isTrue(); 67 | } 68 | 69 | @Test 70 | void pojo_isNotSimple() { 71 | assertThat(ExpanderUtils.isSimpleType(SimpleObject.class)).isFalse(); 72 | 73 | } 74 | 75 | enum Constants { 76 | ONE 77 | } 78 | 79 | private class SimpleObject { 80 | 81 | } 82 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/template/ExpansionPolicy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | /** 20 | * Manages expansion of an Expression. 21 | */ 22 | public class ExpansionPolicy { 23 | 24 | private final String start; 25 | private final String separator; 26 | private final String emptySeparator; 27 | private boolean requiredNamedParameters; 28 | private boolean allowReservedCharacters; 29 | 30 | /** 31 | * Creates a new Expansion Policy. 32 | * 33 | * @param start separator to use. 34 | * @param separator to use in-between variables. 35 | * @param emptySeparator to use when the expanded variable is empty. 36 | * @param requiredNamedParameters if the expansion should include the variable name. 37 | * @param allowReservedCharacters if the expansion should include reserved characters. 38 | */ 39 | public ExpansionPolicy(String start, String separator, String emptySeparator, 40 | boolean requiredNamedParameters, boolean allowReservedCharacters) { 41 | this.start = start; 42 | this.separator = separator; 43 | this.emptySeparator = emptySeparator; 44 | this.requiredNamedParameters = requiredNamedParameters; 45 | this.allowReservedCharacters = allowReservedCharacters; 46 | } 47 | 48 | public String getStartSeparator() { 49 | return start; 50 | } 51 | 52 | public String getSeparator() { 53 | return separator; 54 | } 55 | 56 | public String getEmptySeparator() { 57 | return emptySeparator; 58 | } 59 | 60 | public boolean isRequiredNamedParameters() { 61 | return requiredNamedParameters; 62 | } 63 | 64 | public boolean isAllowReservedCharacters() { 65 | return allowReservedCharacters; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /core/src/main/java/feign/decoder/AbstractResponseDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.decoder; 18 | 19 | import feign.Response; 20 | import feign.ResponseDecoder; 21 | import feign.exception.FeignException; 22 | import java.io.InputStream; 23 | 24 | /** 25 | * Base Class for Response Decoders. 26 | */ 27 | public abstract class AbstractResponseDecoder implements ResponseDecoder { 28 | 29 | /** 30 | * Decode the Response. Provides support for handling common response types. 31 | * 32 | * @param response to decode. 33 | * @param type desired. 34 | * @param defining the type desired. 35 | * @return an instance of the desired type. 36 | */ 37 | @SuppressWarnings("unchecked") 38 | @Override 39 | public T decode(Response response, Class type) { 40 | try { 41 | if (byte[].class.equals(type)) { 42 | return (T) response.toByteArray(); 43 | } else if (InputStream.class.isAssignableFrom(type)) { 44 | /* return a byte array input stream */ 45 | return (T) response.body(); 46 | } 47 | 48 | /* no body */ 49 | if (response == null || response.body() == null) { 50 | return null; 51 | } 52 | 53 | /* dispatch to the sub classes */ 54 | return this.decodeInternal(response, type); 55 | } catch (Exception ex) { 56 | throw new FeignException("Error decoding the Response", ex, 57 | this.getClass().getName() + "#decode"); 58 | } 59 | } 60 | 61 | /** 62 | * Decode the Response. 63 | * 64 | * @param response to decode. 65 | * @param type desired. 66 | * @param defining the type desired 67 | * @return an instance of the desired type. 68 | */ 69 | protected abstract T decodeInternal(Response response, Class type); 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/TypeDrivenMethodHandlerFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl; 18 | 19 | import feign.FeignConfiguration; 20 | import feign.TargetMethodHandler; 21 | import feign.TargetMethodHandlerFactory; 22 | import feign.contract.TargetMethodDefinition; 23 | import feign.impl.type.TypeDefinition; 24 | import java.util.concurrent.Future; 25 | 26 | /** 27 | * Target Method Handler Factory that uses the 28 | * {@link TargetMethodDefinition#getReturnTypeDefinition()} to determine which Method Handler to 29 | * create. 30 | */ 31 | public class TypeDrivenMethodHandlerFactory implements TargetMethodHandlerFactory { 32 | 33 | /** 34 | * Creates a new {@link TargetMethodHandler} based on the return type of the {@link 35 | * TargetMethodDefinition} provided. 36 | * 37 | * @param targetMethodDefinition to inspect. 38 | * @param configuration with the required dependencies. 39 | * @return a new {@link TargetMethodHandler} instance. 40 | */ 41 | @Override 42 | public TargetMethodHandler create(TargetMethodDefinition targetMethodDefinition, 43 | FeignConfiguration configuration) { 44 | 45 | TypeDefinition typeDefinition = targetMethodDefinition.getReturnTypeDefinition(); 46 | if (isFuture(typeDefinition.getType())) { 47 | return new AsyncTargetMethodHandler(targetMethodDefinition, configuration); 48 | } else { 49 | /* return a blocking handler */ 50 | return new BlockingTargetMethodHandler(targetMethodDefinition, configuration); 51 | } 52 | } 53 | 54 | /** 55 | * Determines if the type provided is an implementation of a {@link Future}. 56 | * 57 | * @param type to evaluate. 58 | * @return {@literal true} if the type implements {@link Future}, {@literal false} otherwise. 59 | */ 60 | private boolean isFuture(Class type) { 61 | return Future.class.isAssignableFrom(type); 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /core/src/test/java/feign/decoder/StringDecoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.decoder; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | import static org.mockito.Mockito.when; 22 | 23 | import feign.Response; 24 | import feign.ResponseDecoder; 25 | import feign.exception.FeignException; 26 | import java.io.ByteArrayInputStream; 27 | import java.io.IOException; 28 | import java.nio.charset.StandardCharsets; 29 | import java.util.List; 30 | import org.junit.jupiter.api.Test; 31 | import org.junit.jupiter.api.extension.ExtendWith; 32 | import org.mockito.Mock; 33 | import org.mockito.junit.jupiter.MockitoExtension; 34 | 35 | @ExtendWith(MockitoExtension.class) 36 | class StringDecoderTest { 37 | 38 | private ResponseDecoder decoder = new StringDecoder(); 39 | 40 | @Mock 41 | private Response response; 42 | 43 | @Test 44 | void feignException_whenTypeIsNotString() { 45 | when(response.body()).thenReturn(new ByteArrayInputStream("content".getBytes())); 46 | assertThrows(FeignException.class, () -> decoder.decode(response, List.class)); 47 | } 48 | 49 | @Test 50 | void ioException_whenResponseCouldNotBeRead() throws Exception { 51 | when(response.body()).thenReturn(new ByteArrayInputStream("content".getBytes())); 52 | when(this.response.toByteArray()).thenThrow(new IOException("IO Exception")); 53 | assertThrows(FeignException.class, () -> decoder.decode(response, String.class)); 54 | } 55 | 56 | @Test 57 | void decodeResponse_fromByteArray() throws IOException { 58 | when(response.body()).thenReturn(new ByteArrayInputStream("content".getBytes())); 59 | when(this.response.toByteArray()).thenReturn("content".getBytes(StandardCharsets.UTF_8)); 60 | String result = this.decoder.decode(response, String.class); 61 | assertThat(result).isEqualTo("content"); 62 | } 63 | 64 | } -------------------------------------------------------------------------------- /core/src/test/resources/template/negative-tests.json: -------------------------------------------------------------------------------- 1 | { 2 | "Failure Tests":{ 3 | "level":4, 4 | "variables":{ 5 | "id" : "thing", 6 | "var" : "value", 7 | "hello" : "Hello World!", 8 | "with space" : "fail", 9 | " leading_space" : "Hi!", 10 | "trailing_space " : "Bye!", 11 | "empty" : "", 12 | "path" : "/foo/bar", 13 | "x" : "1024", 14 | "y" : "768", 15 | "list" : ["red", "green", "blue"], 16 | "keys" : { "semi" : ";", "dot" : ".", "comma" : ","}, 17 | "example" : "red", 18 | "searchTerms" : "uri templates", 19 | "~thing" : "some-user", 20 | "default-graph-uri" : ["http://www.example/book/","http://www.example/papers/"], 21 | "query" : "PREFIX dc: SELECT ?book ?who WHERE { ?book dc:creator ?who }" 22 | 23 | }, 24 | "testcases":[ 25 | [ "{/id*", false ], 26 | [ "/id*}", false ], 27 | [ "{/?id}", false ], 28 | [ "{var:prefix}", false ], 29 | [ "{hello:2*}", false ] , 30 | [ "{??hello}", false ] , 31 | [ "{!hello}", false ] , 32 | [ "{with space}", false], 33 | [ "{ leading_space}", false], 34 | [ "{trailing_space }", false], 35 | [ "{=path}", false ] , 36 | [ "{$var}", false ], 37 | [ "{|var*}", false ], 38 | [ "{*keys?}", false ], 39 | [ "{?empty=default,var}", false ], 40 | [ "{var}{-prefix|/-/|var}" , false ], 41 | [ "?q={searchTerms}&c={example:color?}" , false ], 42 | [ "x{?empty|foo=none}" , false ], 43 | [ "/h{#hello+}" , false ], 44 | [ "/h#{hello+}" , false ], 45 | [ "{list:1}", false ], 46 | [ "{keys:1}", false ], 47 | [ "{+keys:1}", false ], 48 | [ "{;keys:1*}", false ], 49 | [ "?{-join|&|var,list}" , false ], 50 | [ "/people/{~thing}", false], 51 | [ "/{default-graph-uri}", false ], 52 | [ "/sparql{?query,default-graph-uri}", false ], 53 | [ "/sparql{?query){&default-graph-uri*}", false ], 54 | [ "/resolution{?x, y}" , false ] 55 | 56 | ] 57 | } 58 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/Request.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract; 18 | 19 | import feign.RequestOptions; 20 | import feign.http.HttpMethod; 21 | import java.lang.annotation.Documented; 22 | import java.lang.annotation.ElementType; 23 | import java.lang.annotation.Inherited; 24 | import java.lang.annotation.Retention; 25 | import java.lang.annotation.RetentionPolicy; 26 | import java.lang.annotation.Target; 27 | 28 | /** 29 | * Annotation that provides the HTTP HttpMethod, URI template, and HttpHeader to apply to this 30 | * request. 31 | */ 32 | @Target({ElementType.TYPE, ElementType.METHOD}) 33 | @Retention(RetentionPolicy.RUNTIME) 34 | @Documented 35 | @Inherited 36 | public @interface Request { 37 | 38 | /** 39 | * Uri for this HttpRequest. 40 | * 41 | * @return the uri. 42 | */ 43 | String value(); 44 | 45 | /** 46 | * HTTP HttpMethod for this request. Defaults to GET. 47 | * 48 | * @return http method. 49 | */ 50 | HttpMethod method() default HttpMethod.GET; 51 | 52 | /** 53 | * Uri for the HttpRequest. Alias for value. 54 | * 55 | * @return the uri. 56 | */ 57 | String uri() default ""; 58 | 59 | /** 60 | * Flag that determines if this request should follow any 3xx response codes automatically. 61 | * Default is {@literal true} 62 | * 63 | * @return if this request should follow redirect responses. 64 | */ 65 | boolean followRedirects() default true; 66 | 67 | /** 68 | * HttpRequest Connection Timeout value, in milliseconds. 69 | * 70 | * @return how long to wait before failing when connecting to the target. 71 | */ 72 | long connectTimeout() default RequestOptions.DEFAULT_CONNECT_TIMEOUT; 73 | 74 | /** 75 | * Read Timeout value, in milliseconds. 76 | * 77 | * @return how long to wait before failing when reading data from the target. 78 | */ 79 | long readTimeout() default RequestOptions.DEFAULT_READ_TIMEOUT; 80 | 81 | } 82 | -------------------------------------------------------------------------------- /core/src/test/java/feign/template/tck/TestGroup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.tck; 18 | 19 | import com.fasterxml.jackson.annotation.JsonProperty; 20 | import feign.template.ExpressionExpander; 21 | import feign.template.SimpleTemplateParameter; 22 | import feign.template.TemplateParameter; 23 | import feign.template.expander.ListExpander; 24 | import feign.template.expander.MapExpander; 25 | import feign.template.expander.SimpleExpander; 26 | import java.util.ArrayList; 27 | import java.util.LinkedHashMap; 28 | import java.util.List; 29 | import java.util.Map; 30 | import java.util.Map.Entry; 31 | 32 | public class TestGroup { 33 | 34 | private String level; 35 | private Map variables; 36 | 37 | @JsonProperty("testcases") 38 | private List testCases; 39 | 40 | protected TestGroup() { 41 | super(); 42 | this.variables = new LinkedHashMap<>(); 43 | this.testCases = new ArrayList<>(); 44 | } 45 | 46 | public String getLevel() { 47 | return level; 48 | } 49 | 50 | public void setLevel(String level) { 51 | this.level = level; 52 | } 53 | 54 | public Map getVariables() { 55 | return variables; 56 | } 57 | 58 | public void setVariables(Map variables) { 59 | for (Entry entry : variables.entrySet()) { 60 | Object value = entry.getValue(); 61 | ExpressionExpander expander = new SimpleExpander(); 62 | if (value instanceof Map) { 63 | expander = new MapExpander(); 64 | } else if (value instanceof Iterable) { 65 | expander = new ListExpander(); 66 | } 67 | 68 | this.variables.put(new SimpleTemplateParameter(entry.getKey(), expander), entry.getValue()); 69 | } 70 | } 71 | 72 | public List getTestCases() { 73 | return testCases; 74 | } 75 | 76 | public void setTestCases(List testCases) { 77 | this.testCases = testCases; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /core/src/main/java/feign/FeignConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import feign.http.RequestSpecification; 20 | import java.util.List; 21 | import java.util.concurrent.Executor; 22 | import java.util.function.Consumer; 23 | 24 | /** 25 | * Configuration Definition. 26 | */ 27 | public interface FeignConfiguration { 28 | 29 | /** 30 | * Consumer that targets a request. 31 | * 32 | * @return a uri supplier. 33 | */ 34 | Consumer getTarget(); 35 | 36 | /** 37 | * Client instance to use to execute requests. 38 | * 39 | * @return a client instance. 40 | */ 41 | Client getClient(); 42 | 43 | /** 44 | * Request Encoder to apply to requests. 45 | * 46 | * @return a request encoder instance. 47 | */ 48 | RequestEncoder getRequestEncoder(); 49 | 50 | /** 51 | * Response Decoder to apply to responses. 52 | * 53 | * @return a response decoder instance. 54 | */ 55 | ResponseDecoder getResponseDecoder(); 56 | 57 | /** 58 | * Contract to apply to Targets. 59 | * 60 | * @return a contract instance. 61 | */ 62 | Contract getContract(); 63 | 64 | /** 65 | * Executor to use when executing requests on a Client. 66 | * 67 | * @return an executor instance. 68 | */ 69 | Executor getExecutor(); 70 | 71 | /** 72 | * Interceptors to apply to requests. 73 | * 74 | * @return a list of interceptor instances. 75 | */ 76 | List getRequestInterceptors(); 77 | 78 | /** 79 | * Exception Handler to apply in case of an exception. 80 | * 81 | * @return an exception handler instance. 82 | */ 83 | ExceptionHandler getExceptionHandler(); 84 | 85 | /** 86 | * Logger to use when logging request and responses. 87 | * 88 | * @return the log instance. 89 | */ 90 | Logger getLogger(); 91 | 92 | /** 93 | * Retry to use when processing requests. 94 | * 95 | * @return the retry instance. 96 | */ 97 | Retry getRetry(); 98 | } 99 | -------------------------------------------------------------------------------- /core/src/test/java/feign/assertions/Assertions.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2022 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.assertions; 18 | 19 | import org.assertj.core.util.introspection.Introspection; 20 | 21 | /** 22 | * Entry point for assertions of different data types. Each method in this class is a static factory for the 23 | * type-specific assertion objects. 24 | */ 25 | @javax.annotation.Generated(value="assertj-assertions-generator") 26 | public class Assertions { 27 | 28 | /** 29 | * Creates a new instance of {@link feign.http.HttpHeaderAssert}. 30 | * 31 | * @param actual the actual value. 32 | * @return the created assertion object. 33 | */ 34 | @org.assertj.core.util.CheckReturnValue 35 | public static feign.assertions.HttpHeaderAssert assertThat(feign.http.HttpHeader actual) { 36 | Introspection.setExtractBareNamePropertyMethods(true); 37 | return new feign.assertions.HttpHeaderAssert(actual); 38 | } 39 | 40 | /** 41 | * Creates a new instance of {@link feign.http.HttpRequestAssert}. 42 | * 43 | * @param actual the actual value. 44 | * @return the created assertion object. 45 | */ 46 | @org.assertj.core.util.CheckReturnValue 47 | public static feign.assertions.HttpRequestAssert assertThat(feign.http.HttpRequest actual) { 48 | Introspection.setExtractBareNamePropertyMethods(true); 49 | return new feign.assertions.HttpRequestAssert(actual); 50 | } 51 | 52 | /** 53 | * Creates a new instance of {@link feign.http.HttpResponseAssert}. 54 | * 55 | * @param actual the actual value. 56 | * @return the created assertion object. 57 | */ 58 | @org.assertj.core.util.CheckReturnValue 59 | public static feign.assertions.HttpResponseAssert assertThat(feign.http.HttpResponse actual) { 60 | Introspection.setExtractBareNamePropertyMethods(true); 61 | return new feign.assertions.HttpResponseAssert(actual); 62 | } 63 | 64 | /** 65 | * Creates a new {@link Assertions}. 66 | */ 67 | protected Assertions() { 68 | // empty 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /core/src/main/java/feign/http/HttpException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.http; 18 | 19 | import feign.Client; 20 | import java.util.Optional; 21 | 22 | /** 23 | * An exception that occurred during a {@link Client} operation. 24 | */ 25 | public class HttpException extends RuntimeException { 26 | 27 | private HttpRequest request; 28 | private HttpResponse response; 29 | 30 | /** 31 | * Creates a new Http Exception. 32 | * 33 | * @param message for the exception. 34 | * @param cause of the exception. 35 | */ 36 | public HttpException(String message, Throwable cause) { 37 | super(message, cause); 38 | } 39 | 40 | /** 41 | * Creates a new Http Exception. 42 | * 43 | * @param message for the exception. 44 | * @param cause of the exception. 45 | * @param request that was attempted. 46 | */ 47 | public HttpException(String message, Throwable cause, HttpRequest request) { 48 | super(message, cause); 49 | this.request = request; 50 | } 51 | 52 | /** 53 | * Creates a new Http Exception. 54 | * 55 | * @param message for the exception. 56 | * @param cause of the exception. 57 | * @param request that was attempted. 58 | * @param response that was received. 59 | */ 60 | public HttpException(String message, Throwable cause, HttpRequest request, 61 | HttpResponse response) { 62 | super(message, cause); 63 | this.request = request; 64 | this.response = response; 65 | } 66 | 67 | /** 68 | * Creates a new HttpException. 69 | * 70 | * @param message fo the exception. 71 | * @param request that was attempted. 72 | * @param response response that was received. 73 | */ 74 | public HttpException(String message, HttpRequest request, HttpResponse response) { 75 | super(message); 76 | this.request = request; 77 | this.response = response; 78 | } 79 | 80 | public Optional getRequest() { 81 | return Optional.ofNullable(this.request); 82 | } 83 | 84 | public Optional getResponse() { 85 | return Optional.ofNullable(this.response); 86 | } 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /core/src/main/java/feign/contract/FeignContract.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.contract; 18 | 19 | import feign.contract.impl.BodyAnnotationProcessor; 20 | import feign.contract.impl.HeadersAnnotationProcessor; 21 | import feign.contract.impl.ParamAnnotationProcessor; 22 | import feign.contract.impl.RequestAnnotationProcessor; 23 | import java.lang.annotation.Annotation; 24 | import java.util.Collection; 25 | import java.util.Set; 26 | 27 | /** 28 | * Contract that uses Feign annotations. 29 | */ 30 | public class FeignContract extends AbstractAnnotationDrivenContract { 31 | 32 | /** 33 | * Creates a new Feign Contract. 34 | */ 35 | public FeignContract() { 36 | super(); 37 | this.registerAnnotationProcessor(Request.class, new RequestAnnotationProcessor()); 38 | this.registerAnnotationProcessor(Headers.class, new HeadersAnnotationProcessor()); 39 | this.registerParameterAnnotationProcessor(Param.class, new ParamAnnotationProcessor()); 40 | this.registerParameterAnnotationProcessor(Body.class, new BodyAnnotationProcessor()); 41 | } 42 | 43 | /** 44 | * Support {@link Request} and {@link Headers} at the class level. 45 | * 46 | * @return set of supported annotations at the class level. 47 | */ 48 | @Override 49 | protected Collection> getSupportedClassAnnotations() { 50 | return Set.of(Request.class, Headers.class); 51 | } 52 | 53 | /** 54 | * Support the same items at the class level at the method level. 55 | * 56 | * @return a set of supported annotations at the method level. 57 | */ 58 | @Override 59 | protected Collection> getSupportedMethodAnnotations() { 60 | return this.getSupportedClassAnnotations(); 61 | } 62 | 63 | /** 64 | * Support the {@link Param} and {@link Body} annotations at the parameter level. 65 | * 66 | * @return the set of supported annotations at the parameter level. 67 | */ 68 | @Override 69 | protected Collection> getSupportedParameterAnnotations() { 70 | return Set.of(Param.class, Body.class); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/BlockingTargetMethodHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl; 18 | 19 | import feign.FeignConfiguration; 20 | import feign.contract.TargetMethodDefinition; 21 | import feign.exception.FeignException; 22 | import java.util.concurrent.CompletableFuture; 23 | import java.util.concurrent.ExecutionException; 24 | 25 | /** 26 | * HttpMethod handler that uses the calling thread to process the request and response. 27 | */ 28 | public class BlockingTargetMethodHandler extends AbstractTargetMethodHandler { 29 | 30 | /** 31 | * Creates a new {@link BlockingTargetMethodHandler}. 32 | * 33 | * @param targetMethodDefinition containing the method configuration. 34 | * @param configuration with the target configuration. 35 | */ 36 | BlockingTargetMethodHandler(TargetMethodDefinition targetMethodDefinition, 37 | FeignConfiguration configuration) { 38 | /* create a new method handler, with a synchronous executor */ 39 | super(targetMethodDefinition, configuration); 40 | } 41 | 42 | /** 43 | * Blocks the calling thread, waiting for the result of the request. 44 | * 45 | * @param result being processed. 46 | * @return the decoded response. 47 | */ 48 | @Override 49 | protected Object handleResponse(CompletableFuture result) { 50 | try { 51 | /* pull the result of the task immediately, waiting for it to complete */ 52 | return result.get(); 53 | } catch (ExecutionException eex) { 54 | /* an error occurred. by this point the error handler should have been called 55 | * already, check the cause of the exception and throw it higher. 56 | */ 57 | Throwable cause = eex.getCause(); 58 | 59 | /* just rethrow, the exception handler must throw a RuntimeException since it has 60 | * no checked exceptions in it's signature. */ 61 | throw (RuntimeException) cause; 62 | 63 | } catch (InterruptedException ie) { 64 | /* the request was interrupted */ 65 | throw new FeignException(ie.getMessage(), ie, this.targetMethodDefinition.getTag()); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /core/src/test/java/feign/template/UriTemplateTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | 21 | import feign.template.expander.ListExpander; 22 | import feign.template.expander.MapExpander; 23 | import java.net.URI; 24 | import java.util.Arrays; 25 | import java.util.Collections; 26 | import java.util.LinkedHashMap; 27 | import java.util.Map; 28 | import org.junit.jupiter.api.Test; 29 | 30 | class UriTemplateTest { 31 | 32 | @Test 33 | void create_withLiterals() { 34 | UriTemplate template = UriTemplate.create("https://www.example.com/path?parameter=value"); 35 | assertThat(template).isNotNull(); 36 | assertThat(template.getExpressions()).isEmpty(); 37 | } 38 | 39 | @Test 40 | void theKitchenSink() throws Exception { 41 | UriTemplate template = 42 | UriTemplate.create("{+scheme}www{.host*}{/path*}{;params}{?query*}{&cont*}{#fragment}"); 43 | assertThat(template).isNotNull(); 44 | assertThat(template.getExpressions()).isNotEmpty(); 45 | 46 | /* expand it */ 47 | Map variables = new LinkedHashMap<>(); 48 | variables.put(new SimpleTemplateParameter("scheme"), "https://"); 49 | variables.put(new SimpleTemplateParameter("host", new ListExpander()), Arrays.asList("example", "com")); 50 | variables.put(new SimpleTemplateParameter("path", new ListExpander()), Arrays.asList("resources","items")); 51 | variables.put(new SimpleTemplateParameter("params", new MapExpander()), Collections.singletonMap("filter", "name")); 52 | variables.put(new SimpleTemplateParameter("query", new MapExpander()), Collections.singletonMap("sort", "descending")); 53 | variables.put(new SimpleTemplateParameter("cont", new MapExpander()), Collections.singletonMap("page", "0")); 54 | variables.put(new SimpleTemplateParameter("fragment"), "total"); 55 | 56 | URI result = template.expand(variables); 57 | assertThat(result).isNotNull(); 58 | assertThat(result.toString()) 59 | .isEqualTo("https://www.example.com/resources/items;params=filter,name?" 60 | + "sort=descending&page=0#total"); 61 | assertThat(result.toURL()).isNotNull(); 62 | } 63 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/template/expander/SimpleExpander.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.expander; 18 | 19 | import feign.template.Expression; 20 | import feign.template.ExpressionExpander; 21 | import feign.template.ExpressionVariable; 22 | import feign.template.UriUtils; 23 | 24 | /** 25 | * Expression Expander that relies on the values {@link Object#toString()}. This expander will 26 | * honor any prefix limits. 27 | */ 28 | public class SimpleExpander implements ExpressionExpander { 29 | 30 | /* Singleton instantiation */ 31 | private static final SimpleExpander instance = new SimpleExpander(); 32 | 33 | public static SimpleExpander getInstance() { 34 | return instance; 35 | } 36 | 37 | /** 38 | * Expand the given expression, using the value provided. 39 | * 40 | * @param variable to expand. 41 | * @param value containing the variable values. 42 | * @return the expanded result, an empty string, or {@literal null}. 43 | */ 44 | @Override 45 | public String expand(ExpressionVariable variable, Object value) { 46 | if (value == null) { 47 | /* skip unresolved */ 48 | return null; 49 | } 50 | 51 | /* expand the value */ 52 | String result = value.toString(); 53 | 54 | /* build the expanded expression */ 55 | Expression expression = variable.getExpression(); 56 | StringBuilder expanded = new StringBuilder(); 57 | if (expression.requiredNamedParameters()) { 58 | expanded.append(this.encode(variable.getName(), expression.allowReservedCharacters())); 59 | if (result.isEmpty()) { 60 | expanded.append(expression.getEmptySeparator()); 61 | } else { 62 | expanded.append("="); 63 | } 64 | } 65 | 66 | /* apply any limits */ 67 | if (variable.getPrefix() > 0) { 68 | /* prefix the result */ 69 | int limit = Math.min(variable.getPrefix(), result.length()); 70 | result = result.substring(0, limit); 71 | } 72 | expanded.append(this.encode(result, expression.allowReservedCharacters())); 73 | 74 | /* return the pct-encoded result */ 75 | return expanded.toString(); 76 | } 77 | 78 | String encode(String value, boolean allowReservedCharacters) { 79 | return UriUtils.encode(value, allowReservedCharacters); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/SimpleTemplateParameter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template; 18 | 19 | import feign.support.Assert; 20 | import feign.template.expander.SimpleExpander; 21 | import java.util.Objects; 22 | 23 | /** 24 | * Template Parameter implementation that acts as a simple value object. 25 | */ 26 | public class SimpleTemplateParameter implements TemplateParameter { 27 | 28 | private final String name; 29 | private ExpressionExpander expander = new SimpleExpander(); 30 | 31 | /** 32 | * Creates a new {@link SimpleTemplateParameter}. 33 | * 34 | * @param name of the parameter. 35 | * @throws IllegalArgumentException if the name is {@literal null} or empty. 36 | */ 37 | public SimpleTemplateParameter(String name) { 38 | Assert.isNotEmpty(name, "name is required"); 39 | this.name = name; 40 | } 41 | 42 | /** 43 | * Creates a new {@link SimpleTemplateParameter}. 44 | * 45 | * @param name of the parameter. 46 | * @param expander to use when expanding the expression contained. 47 | */ 48 | public SimpleTemplateParameter(String name, ExpressionExpander expander) { 49 | this(name); 50 | Assert.isNotNull(expander, "expander is required."); 51 | this.expander = expander; 52 | } 53 | 54 | /** 55 | * The name of the parameter. 56 | * 57 | * @return parameter name. 58 | */ 59 | @Override 60 | public String name() { 61 | return this.name; 62 | } 63 | 64 | /** 65 | * {@link ExpressionExpander} instance to use when expanding this parameter. 66 | * 67 | * @return the expander to use during expansion. 68 | */ 69 | @Override 70 | public ExpressionExpander expander() { 71 | return this.expander; 72 | } 73 | 74 | /** 75 | * {@inheritDoc} 76 | */ 77 | @Override 78 | public boolean equals(Object obj) { 79 | if (this == obj) { 80 | return true; 81 | } 82 | if (!(obj instanceof SimpleTemplateParameter)) { 83 | return false; 84 | } 85 | SimpleTemplateParameter that = (SimpleTemplateParameter) obj; 86 | return name.toLowerCase().equals(that.name.toLowerCase()); 87 | } 88 | 89 | /** 90 | * {@inheritDoc} 91 | */ 92 | @Override 93 | public int hashCode() { 94 | return Objects.hash(name.toLowerCase()); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/type/ParameterizedTypeDefinition.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl.type; 18 | 19 | import java.lang.reflect.ParameterizedType; 20 | import java.lang.reflect.Type; 21 | 22 | /** 23 | * Type Definition for a {@link ParameterizedType}, the simplest type of Generic. 24 | */ 25 | public class ParameterizedTypeDefinition extends AbstractTypeDefinition implements 26 | ParameterizedType { 27 | 28 | private final Type owner; 29 | private final Type raw; 30 | private final TypeDefinition[] arguments; 31 | 32 | /** 33 | * Creates a new ParameterizedTypeDefinition. 34 | * 35 | * @param owner of the type, in the event this ParameterizedType is contained within another. 36 | * @param raw type contained. 37 | * @param arguments for each TypeVariable in the type definition. 38 | */ 39 | ParameterizedTypeDefinition(Type owner, Type raw, TypeDefinition... arguments) { 40 | this.owner = owner; 41 | this.raw = raw; 42 | this.arguments = arguments.clone(); 43 | } 44 | 45 | /** 46 | * Return the type declared. 47 | * 48 | * @return the actual type being managed. 49 | */ 50 | @Override 51 | public Class getActualType() { 52 | /* use the first type argument here */ 53 | TypeDefinition actualType = this.arguments[0]; 54 | return actualType.getType(); 55 | } 56 | 57 | /** 58 | * This type defined. 59 | * 60 | * @return the defined type. 61 | */ 62 | @Override 63 | public Class getType() { 64 | /* use the raw type */ 65 | return (Class) this.getRawType(); 66 | } 67 | 68 | /** 69 | * Return the Type Arguments defined. 70 | * 71 | * @return an array of Types 72 | */ 73 | @Override 74 | public Type[] getActualTypeArguments() { 75 | return arguments.clone(); 76 | } 77 | 78 | /** 79 | * The Raw type representing the class or interface declared. 80 | * 81 | * @return the declared type. 82 | */ 83 | @Override 84 | public Type getRawType() { 85 | return this.raw; 86 | } 87 | 88 | /** 89 | * The Type representing the that this type is a member of. 90 | * 91 | * @return the owner type, can be {@literal null}. 92 | */ 93 | @Override 94 | public Type getOwnerType() { 95 | return this.owner; 96 | } 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /core/src/main/java/feign/template/expander/ListExpander.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.expander; 18 | 19 | import feign.support.Assert; 20 | import feign.template.ExpansionPolicy; 21 | 22 | /** 23 | * Expression Expander that support List, Collection and other {@link Iterable} types. 24 | */ 25 | public class ListExpander extends MultiValueExpander { 26 | 27 | /* Singleton instantiation */ 28 | private static final ListExpander instance = new ListExpander(); 29 | 30 | public static ListExpander getInstance() { 31 | return instance; 32 | } 33 | 34 | @Override 35 | protected Iterable getValues(Object value) { 36 | /* verify the value is an iterable type */ 37 | Assert.isTrue(value, 38 | obj -> Iterable.class.isAssignableFrom(obj.getClass()), 39 | "Type " + value.getClass() 40 | + " is not supported by this expander. Values must be a List, Collection or " 41 | + "extend Iterable."); 42 | 43 | return (Iterable) value; 44 | } 45 | 46 | /** 47 | * "Explodes" the value into a name,value pair per the specification. 48 | * 49 | * @param name of the value being expanded. 50 | * @param value to explode. 51 | * @param policy to apply when expanding the value. 52 | * @return the exploded expanded result. 53 | */ 54 | @Override 55 | protected String explode(String name, Object value, ExpansionPolicy policy) { 56 | String expanded = value.toString(); 57 | StringBuilder result = new StringBuilder(); 58 | if (policy.isRequiredNamedParameters()) { 59 | result.append(this.encode(name, policy.isAllowReservedCharacters())); 60 | if (expanded.isEmpty()) { 61 | result.append(policy.getEmptySeparator()); 62 | } else { 63 | result.append("="); 64 | } 65 | } 66 | 67 | result.append(this.encode(expanded, policy.isAllowReservedCharacters())); 68 | return result.toString(); 69 | } 70 | 71 | /** 72 | * Expands the value. 73 | * 74 | * @param name of the value being expanded. 75 | * @param value to expand. 76 | * @param policy to apply when expanding the value. 77 | * @return the value provided expanded per the policy. 78 | */ 79 | @Override 80 | protected String expand(String name, Object value, ExpansionPolicy policy) { 81 | return this.encode(value.toString(), policy.isAllowReservedCharacters()); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /core/src/test/java/feign/TargetMethodDefinitionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import static org.mockito.Mockito.mock; 21 | 22 | import feign.contract.TargetMethodDefinition; 23 | import org.junit.jupiter.api.Test; 24 | 25 | class TargetMethodDefinitionTest { 26 | 27 | @Test 28 | void getUri_alwaysReturnsAValue() { 29 | TargetMethodDefinition targetMethodDefinition = 30 | TargetMethodDefinition.builder(Target.class.getName()) 31 | .build(); 32 | assertThat(targetMethodDefinition.getUri()).isNotNull(); 33 | } 34 | 35 | @Test 36 | void equals_name_caseSensitive() { 37 | TargetMethodDefinition targetMethodDefinition = 38 | TargetMethodDefinition.builder(Target.class.getName()) 39 | .name("name") 40 | .build(); 41 | 42 | TargetMethodDefinition anotherDefinition = 43 | TargetMethodDefinition.builder(Target.class.getName()) 44 | .name("name") 45 | .build(); 46 | assertThat(targetMethodDefinition).isEqualTo(anotherDefinition); 47 | } 48 | 49 | @Test 50 | void equals_itself() { 51 | TargetMethodDefinition targetMethodDefinition = 52 | TargetMethodDefinition.builder(Target.class.getName()) 53 | .build(); 54 | assertThat(targetMethodDefinition).isEqualTo(targetMethodDefinition); 55 | } 56 | 57 | @Test 58 | void notEqual_toOtherTypes() { 59 | TargetMethodDefinition targetMethodDefinition = 60 | TargetMethodDefinition.builder(Target.class.getName()) 61 | .build(); 62 | assertThat(targetMethodDefinition).isNotEqualTo("A String"); 63 | } 64 | 65 | @Test 66 | void notEqual_name_caseSensitive() { 67 | TargetMethodDefinition targetMethodDefinition = 68 | TargetMethodDefinition.builder(Target.class.getName()) 69 | .name("name") 70 | .build(); 71 | 72 | TargetMethodDefinition anotherDefinition = 73 | TargetMethodDefinition.builder(Target.class.getName()) 74 | .name("Name") 75 | .build(); 76 | assertThat(targetMethodDefinition).isNotEqualTo(anotherDefinition); 77 | } 78 | 79 | @Test 80 | void toString_isNotEmpty() { 81 | TargetMethodDefinition targetMethodDefinition = 82 | TargetMethodDefinition.builder(Target.class.getName()).build(); 83 | assertThat(targetMethodDefinition.toString()).isNotEmpty(); 84 | } 85 | } -------------------------------------------------------------------------------- /core/src/test/java/feign/decoder/AbstractResponseDecoderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.decoder; 18 | 19 | import static org.assertj.core.api.Assertions.assertThat; 20 | import static org.junit.jupiter.api.Assertions.assertThrows; 21 | import static org.mockito.Mockito.times; 22 | import static org.mockito.Mockito.verify; 23 | import static org.mockito.Mockito.when; 24 | 25 | import feign.Response; 26 | import feign.ResponseDecoder; 27 | import feign.exception.FeignException; 28 | import java.io.ByteArrayInputStream; 29 | import java.io.FileInputStream; 30 | import java.nio.charset.StandardCharsets; 31 | import org.junit.jupiter.api.Test; 32 | import org.junit.jupiter.api.extension.ExtendWith; 33 | import org.mockito.Mock; 34 | import org.mockito.junit.jupiter.MockitoExtension; 35 | 36 | @ExtendWith(MockitoExtension.class) 37 | class AbstractResponseDecoderTest { 38 | 39 | private final ResponseDecoder decoder = new TestResponseDecoder(); 40 | 41 | @Mock 42 | private Response response; 43 | 44 | @Test 45 | void skipDecode_whenByteArray() throws Exception{ 46 | this.decoder.decode(this.response, byte[].class); 47 | verify(response, times(1)).toByteArray(); 48 | } 49 | 50 | @Test 51 | void skipDecode_whenInputStream() { 52 | this.decoder.decode(this.response, FileInputStream.class); 53 | verify(response, times(1)).body(); 54 | } 55 | 56 | @Test 57 | void skipDecode_whenResponseIsNull() { 58 | Object result = this.decoder.decode(null, Object.class); 59 | assertThat(result).isNull(); 60 | } 61 | 62 | @Test 63 | void skipDecode_whenResponseBodyIsNull() { 64 | Object result = this.decoder.decode(this.response, Object.class); 65 | assertThat(result).isNull(); 66 | } 67 | 68 | @Test 69 | void allOthers_Decode() { 70 | when(this.response.body()).thenReturn(new ByteArrayInputStream("results".getBytes( 71 | StandardCharsets.UTF_8))); 72 | String result = this.decoder.decode(this.response, String.class); 73 | assertThat(result).isEqualTo("result"); 74 | } 75 | 76 | @Test 77 | void error_throwsFeignException() { 78 | when(this.response.body()).thenThrow(new RuntimeException("Input Stream Closed.")); 79 | assertThrows(FeignException.class, () -> decoder.decode(response, String.class)); 80 | } 81 | 82 | @SuppressWarnings("unchecked") 83 | static class TestResponseDecoder extends AbstractResponseDecoder { 84 | 85 | @Override 86 | protected T decodeInternal(Response response, Class type) { 87 | return (T) "result"; 88 | } 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/impl/type/TypeUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2021 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.impl.type; 18 | 19 | /** 20 | * Utility Class for working with Types and Class Name. 21 | */ 22 | public class TypeUtils { 23 | 24 | /** 25 | * Create a new Class instance for the class name provided. 26 | * 27 | * @param fullyQualifiedClassName to parse. 28 | * @return the class instance, if found. 29 | * @throws IllegalStateException if the class could not be found. 30 | */ 31 | public static Class getInstance(String fullyQualifiedClassName) { 32 | try { 33 | return Class.forName(fullyQualifiedClassName); 34 | } catch (ClassNotFoundException cnfe) { 35 | if (isPrimitiveType(fullyQualifiedClassName)) { 36 | return getPrimitiveInstance(fullyQualifiedClassName); 37 | } 38 | throw new IllegalStateException("Error occurred obtaining class instance.", cnfe); 39 | } 40 | } 41 | 42 | private static boolean isPrimitiveType(String type) { 43 | return Boolean.TYPE.getName().equalsIgnoreCase(type) 44 | || Character.TYPE.getName().equalsIgnoreCase(type) 45 | || Byte.TYPE.getName().equalsIgnoreCase(type) 46 | || Short.TYPE.getName().equalsIgnoreCase(type) 47 | || Integer.TYPE.getName().equalsIgnoreCase(type) 48 | || Long.TYPE.getName().equalsIgnoreCase(type) 49 | || Float.TYPE.getName().equalsIgnoreCase(type) 50 | || Double.TYPE.getName().equalsIgnoreCase(type) 51 | || Void.TYPE.getName().equalsIgnoreCase(type); 52 | } 53 | 54 | private static Class getPrimitiveInstance(String type) { 55 | if (Boolean.TYPE.getName().equalsIgnoreCase(type)) { 56 | return Boolean.TYPE; 57 | } else if (Character.TYPE.getName().equalsIgnoreCase(type)) { 58 | return Character.TYPE; 59 | } else if (Byte.TYPE.getName().equalsIgnoreCase(type)) { 60 | return Byte.TYPE; 61 | } else if (Short.TYPE.getName().equalsIgnoreCase(type)) { 62 | return Short.TYPE; 63 | } else if (Integer.TYPE.getName().equalsIgnoreCase(type)) { 64 | return Integer.TYPE; 65 | } else if (Long.TYPE.getName().equalsIgnoreCase(type)) { 66 | return Long.TYPE; 67 | } else if (Float.TYPE.getName().equalsIgnoreCase(type)) { 68 | return Float.TYPE; 69 | } else if (Double.TYPE.getName().equalsIgnoreCase(type)) { 70 | return Double.TYPE; 71 | } else if (Void.TYPE.getName().equalsIgnoreCase(type)) { 72 | return Void.TYPE; 73 | } 74 | throw new IllegalArgumentException("Not a primitive type " + type); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /core/src/test/java/feign/http/HttpHeaderTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.http; 18 | 19 | import static feign.assertions.HttpHeaderAssert.assertThat; 20 | import static org.assertj.core.api.Assertions.assertThat; 21 | import static org.junit.jupiter.api.Assertions.*; 22 | 23 | import java.util.Arrays; 24 | import java.util.Collections; 25 | import org.junit.jupiter.api.Test; 26 | 27 | class HttpHeaderTest { 28 | 29 | @Test 30 | void canCreate_withSingleValue() { 31 | HttpHeader httpHeader = 32 | new HttpHeader("Content-Length", Collections.singletonList("123456")); 33 | assertThat(httpHeader).hasName("Content-Length") 34 | .hasValues("123456"); 35 | } 36 | 37 | @Test 38 | void cannotCreate_whenMultipleValuesAreNotAllowed() { 39 | assertThrows(IllegalStateException.class, () -> 40 | new HttpHeader("Content-Length", Arrays.asList("123456", "987654"))); 41 | } 42 | 43 | @Test 44 | void canClear() { 45 | HttpHeader httpHeader = 46 | new HttpHeader("Content-Length", Collections.singletonList("123456")); 47 | httpHeader.clear(); 48 | assertThat(httpHeader).hasNoValues(); 49 | } 50 | 51 | @Test 52 | void canAddNewValue_WhenMultipleIsAllowed() { 53 | HttpHeader httpHeader = 54 | new HttpHeader("Accept", Collections.singletonList("application/json")); 55 | httpHeader.value("text/html"); 56 | assertThat(httpHeader).hasValues("application/json", "text/html"); 57 | } 58 | 59 | @Test 60 | void cannotAddNewValue_WhenMultipleIsNotAllowed() { 61 | HttpHeader httpHeader = 62 | new HttpHeader("Content-Type", Collections.singletonList("application/json")); 63 | assertThrows(IllegalStateException.class, () -> httpHeader.value("text/html")); 64 | } 65 | 66 | @Test 67 | void headersAreEqual_toItself() { 68 | HttpHeader httpHeader = new HttpHeader("X-Custom"); 69 | assertThat(httpHeader).isEqualTo(httpHeader); 70 | } 71 | 72 | 73 | @Test 74 | void headersAreEqual_whenNamesMatch() { 75 | HttpHeader httpHeader = new HttpHeader("X-Custom"); 76 | assertThat(httpHeader).isEqualTo(new HttpHeader("X-Custom")); 77 | } 78 | 79 | @Test 80 | void headersAreNotEqual_whenNotAHeader() { 81 | HttpHeader httpHeader = new HttpHeader("X-Custom"); 82 | assertThat(httpHeader).isNotEqualTo("String"); 83 | assertThat(httpHeader).isNotEqualTo(null); 84 | } 85 | 86 | @Test 87 | void headerToString_isNotNull() { 88 | HttpHeader httpHeader = new HttpHeader("X-Custom"); 89 | assertThat(httpHeader.toString()).isNotEmpty(); 90 | } 91 | 92 | } -------------------------------------------------------------------------------- /core/src/main/java/feign/template/expander/MapExpander.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.template.expander; 18 | 19 | import feign.support.Assert; 20 | import feign.template.ExpansionPolicy; 21 | import java.util.Map; 22 | 23 | /** 24 | * Expression Expander that operates on associative arrays defined as {@link java.util.Map}s. 25 | */ 26 | public class MapExpander extends ListExpander { 27 | 28 | /* Singleton instantiation */ 29 | private static final MapExpander instance = new MapExpander(); 30 | 31 | public static MapExpander getInstance() { 32 | return instance; 33 | } 34 | 35 | /** 36 | * Returns the underlying entry set for the map. 37 | * 38 | * @param value containing the map. 39 | * @return the underlying entry set for the map. 40 | */ 41 | @Override 42 | protected Iterable getValues(Object value) { 43 | /* verify that the value is a map */ 44 | Assert.isTrue(value, 45 | obj -> Map.class.isAssignableFrom(obj.getClass()), 46 | "Type " + value.getClass() 47 | + " is not supported by this expander. Values must be a Map."); 48 | 49 | return ((Map) value).entrySet(); 50 | } 51 | 52 | /** 53 | * {@inheritDoc} 54 | */ 55 | @Override 56 | protected String explode(String name, Object value, ExpansionPolicy policy) { 57 | Map.Entry entry = (Map.Entry) value; 58 | 59 | /* join pairs with an equals */ 60 | return this.expand( 61 | entry.getKey().toString(), entry.getValue().toString(), "=", policy); 62 | } 63 | 64 | /** 65 | * {@inheritDoc} 66 | */ 67 | @Override 68 | protected String expand(String name, Object value, ExpansionPolicy policy) { 69 | Map.Entry entry = (Map.Entry) value; 70 | 71 | /* join using a comma */ 72 | return this.expand( 73 | entry.getKey().toString(), entry.getValue().toString(), ",", policy); 74 | } 75 | 76 | /** 77 | * Expand a name,value pair. 78 | * 79 | * @param name of the pair. 80 | * @param value in the pair. 81 | * @param entrySeparator to use when joining the pair. 82 | * @param policy to use when expanding the value. 83 | * @return the expanded name,value pair. 84 | */ 85 | private String expand(String name, String value, String entrySeparator, ExpansionPolicy policy) { 86 | StringBuilder result = new StringBuilder(); 87 | result.append(this.encode(name, policy.isAllowReservedCharacters())); 88 | if (value.isEmpty()) { 89 | result.append(policy.getEmptySeparator()); 90 | } else { 91 | result.append(entrySeparator); 92 | } 93 | result.append(this.encode(value, policy.isAllowReservedCharacters())); 94 | return result.toString(); 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /core/src/main/java/feign/proxy/GuardMethodHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.proxy; 18 | 19 | import feign.Target; 20 | import feign.TargetMethodHandler; 21 | import feign.support.Assert; 22 | import java.lang.invoke.MethodHandle; 23 | import java.lang.invoke.MethodHandles.Lookup; 24 | import java.lang.reflect.Constructor; 25 | import java.lang.reflect.InvocationTargetException; 26 | import java.lang.reflect.Method; 27 | 28 | /** 29 | * Target HttpMethod Handler implementation for {@code default} or Guard method. 30 | *

31 | * This class uses certain parts of the JDK reflection API that may be considered unsafe. 32 | * In JDK 9+, this type of access frowned upon and may be explicitly disabled in any 33 | * JDK 11+. Until a more complete solution appears, we will continue to use this approach. 34 | *

35 | */ 36 | public class GuardMethodHandler implements TargetMethodHandler { 37 | 38 | private final MethodHandle guardMethodHandle; 39 | 40 | /** 41 | * Creates a new Guard HttpMethod Handler. 42 | * 43 | * @param method to proxy. 44 | * @param target instance this method is for. 45 | * @param proxy to bind this handler to. 46 | */ 47 | GuardMethodHandler(Method method, Target target, Object proxy) { 48 | Assert.isNotNull(method, "method is required."); 49 | Assert.isNotNull(target, "target is required."); 50 | try { 51 | /* attempt to create a new instance of the target type */ 52 | Class targetType = target.type(); 53 | Constructor constructor = Lookup.class.getDeclaredConstructor(Class.class); 54 | 55 | /* this is the line that breaks on JDK 9+, it violates the new security rules */ 56 | constructor.setAccessible(true); 57 | 58 | /* create a temporary instance of the target and execute the method */ 59 | this.guardMethodHandle = constructor.newInstance(targetType) 60 | .in(targetType) 61 | .unreflectSpecial(method, targetType) 62 | .bindTo(proxy); 63 | } catch (InstantiationException | InvocationTargetException | NoSuchMethodException 64 | | IllegalAccessException ie) { 65 | /* either the type does not expose a type that can be instantiated or 66 | * access to the type has been explicitly denied 67 | */ 68 | throw new IllegalStateException(ie); 69 | } 70 | } 71 | 72 | /** 73 | * Execute the HttpMethod Handler. 74 | * 75 | * @param args for the method. 76 | * @return the result of the method. 77 | * @throws Throwable in the event of any exceptions during execution. 78 | */ 79 | @Override 80 | public Object execute(Object[] args) throws Throwable { 81 | return this.guardMethodHandle.invokeWithArguments(args); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FeignX 2 | 3 | [![CircleCI](https://circleci.com/gh/OpenFeign/feignx/tree/master.svg?style=svg)](https://circleci.com/gh/OpenFeign/feignx/tree/master) ![codecov](https://codecov.io/gh/OpenFeign/feignx/branch/master/graph/badge.svg) 4 | [![Known Vulnerabilities](https://snyk.io/test/github/openfeign/feignx/badge.svg)](https://snyk.io/test/github/openfeign/feignx) 5 | 6 | FeignX, an experimental version of [Feign](https://github.com/OpenFeign/feign), extending the 7 | core concepts of Feign beyond HTTP and REST including support for pooled, asynchronous, non-blocking, 8 | and reactive execution modes. 9 | 10 | ## Getting Started 11 | 12 | These instructions will get you a copy of the project up and running on your local machine for 13 | development and testing purposes. 14 | 15 | ### Prerequisites 16 | 17 | What things you need: 18 | 19 | * [Recent Java JDK: (11 or higher)](https://adoptopenjdk.net/) 20 | * A clone of this repository: 21 | 22 | ``` 23 | git clone https://github.com/openfeign/feignx.git 24 | cd feignx 25 | ``` 26 | 27 | ### Building from the Command Line 28 | 29 | To compile, test, and build all artifacts, use: 30 | 31 | ``` 32 | ./mvnw install 33 | ``` 34 | 35 | The first time you run the build, it may take some time to download Maven and all of the required 36 | dependencies, compile, and run all of the tests. Dependencies will be cached in your `$HOME/.m2` 37 | directory. 38 | 39 | ### Importing into your IDE 40 | 41 | Most popular IDE environments support reading project information from a Maven `pom.xml` file. 42 | Follow the instructions for your preferred IDE: 43 | 44 | * [JetBrains IntelliJ](https://www.jetbrains.com/help/idea/maven-support.html) 45 | * [Eclipse](https://books.sonatype.com/m2eclipse-book/reference/creating-sect-importing-projects.html) 46 | * [Visual Studio Code](https://code.visualstudio.com/docs/java/java-project) 47 | 48 | ## Running the tests 49 | 50 | To test your changes, run the test suite using: 51 | 52 | ``` 53 | ./mvnw clean test 54 | ``` 55 | 56 | This will run the entire test suite, verifying your changes including formatting, licensing, and 57 | code coverage. 58 | 59 | ## Built With 60 | 61 | * [Maven](https://maven.apache.org/) - Dependency Management 62 | * [Snyk](https://snyk.io/) - Security and Dependency Scanning 63 | * [Circle CI](https://circleci.com) - Continuous Integration and Deployment 64 | * [Codecov](https://codecov.io) - Code Coverage tracking and reporting 65 | 66 | ## Issues 67 | 68 | To report an issue, or check on the status of an issue, see the [Issues](https://github.com/openfeign/feignx/issues) 69 | section of this repository. 70 | 71 | ## Contributing 72 | 73 | Please read [CONTRIBUTING](CONTRIBUTING.md) for details on our [Code of Conduct](CODE_OF_CONDUCT.md), 74 | and the process for submitting [Pull Requests](https://github.com/openfeign/feignx/pulls) to us. 75 | 76 | ## Releases 77 | 78 | For the versions available, see the [tags on this repository](https://github.com/openfeign/feignx/tags) 79 | or [Maven Central](https://search.maven.org/search?q=g:io.github.openfeign) 80 | 81 | ## Contributors 82 | 83 | See also the list of [contributors](https://github.com/openfeign/contributors) who participated in this project. 84 | 85 | ## License 86 | 87 | This project is licensed under the Apache 2.0 License - see the [LICENSE](LICENSE.md) file for details 88 | 89 | ## Acknowledgments 90 | 91 | * [Netflix OSS](https://netflix.github.io/) 92 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/java,maven,intellij+iml 3 | # Edit at https://www.gitignore.io/?templates=java,maven,intellij+iml 4 | 5 | ### Intellij+iml ### 6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 8 | 9 | # Ignore the entire directory 10 | .idea/ 11 | 12 | # Gradle and Maven with auto-import 13 | # When using Gradle or Maven with auto-import, you should exclude module files, 14 | # since they will be recreated, and may cause churn. Uncomment if using 15 | # auto-import. 16 | # .idea/modules.xml 17 | # .idea/*.iml 18 | # .idea/modules 19 | 20 | # CMake 21 | cmake-build-*/ 22 | 23 | # Mongo Explorer plugin 24 | .idea/**/mongoSettings.xml 25 | 26 | # File-based project format 27 | *.iws 28 | 29 | # IntelliJ 30 | out/ 31 | 32 | # mpeltonen/sbt-idea plugin 33 | .idea_modules/ 34 | 35 | # JIRA plugin 36 | atlassian-ide-plugin.xml 37 | 38 | # Cursive Clojure plugin 39 | .idea/replstate.xml 40 | 41 | # Crashlytics plugin (for Android Studio and IntelliJ) 42 | com_crashlytics_export_strings.xml 43 | crashlytics.properties 44 | crashlytics-build.properties 45 | fabric.properties 46 | 47 | # Editor-based Rest Client 48 | .idea/httpRequests 49 | 50 | # Android studio 3.1+ serialized cache file 51 | .idea/caches/build_file_checksums.ser 52 | 53 | ### Intellij+iml Patch ### 54 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 55 | 56 | *.iml 57 | modules.xml 58 | .idea/misc.xml 59 | *.ipr 60 | 61 | ### Java ### 62 | # Compiled class file 63 | *.class 64 | 65 | # Log file 66 | *.log 67 | 68 | # BlueJ files 69 | *.ctxt 70 | 71 | # Mobile Tools for Java (J2ME) 72 | .mtj.tmp/ 73 | 74 | # Package Files # 75 | *.jar 76 | *.war 77 | *.nar 78 | *.ear 79 | *.zip 80 | *.tar.gz 81 | *.rar 82 | 83 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 84 | hs_err_pid* 85 | 86 | ### Maven ### 87 | target/ 88 | pom.xml.tag 89 | pom.xml.releaseBackup 90 | pom.xml.versionsBackup 91 | pom.xml.next 92 | release.properties 93 | dependency-reduced-pom.xml 94 | buildNumber.properties 95 | .mvn/timing.properties 96 | !.mvn/wrapper/maven-wrapper.jar 97 | 98 | # End of https://www.gitignore.io/api/java,maven,intellij+iml 99 | 100 | # Vagrant 101 | .vagrant/ 102 | 103 | ### macOS ### 104 | # General 105 | .DS_Store 106 | .AppleDouble 107 | .LSOverride 108 | 109 | # Icon must end with two \r 110 | Icon 111 | 112 | # Thumbnails 113 | ._* 114 | 115 | # Files that might appear in the root of a volume 116 | .DocumentRevisions-V100 117 | .fseventsd 118 | .Spotlight-V100 119 | .TemporaryItems 120 | .Trashes 121 | .VolumeIcon.icns 122 | .com.apple.timemachine.donotpresent 123 | 124 | # Directories potentially created on remote AFP share 125 | .AppleDB 126 | .AppleDesktop 127 | Network Trash Folder 128 | Temporary Items 129 | .apdisk 130 | 131 | ### Windows ### 132 | # Windows thumbnail cache files 133 | Thumbs.db 134 | Thumbs.db:encryptable 135 | ehthumbs.db 136 | ehthumbs_vista.db 137 | 138 | # Dump file 139 | *.stackdump 140 | 141 | # Folder config file 142 | [Dd]esktop.ini 143 | 144 | # Recycle Bin used on file shares 145 | $RECYCLE.BIN/ 146 | 147 | # Windows Installer files 148 | *.cab 149 | *.msi 150 | *.msix 151 | *.msm 152 | *.msp 153 | 154 | # Windows shortcuts 155 | *.lnk -------------------------------------------------------------------------------- /core/src/main/java/feign/support/Assert.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019-2020 OpenFeign Contributors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package feign.support; 18 | 19 | import java.util.Collection; 20 | import java.util.function.Predicate; 21 | 22 | /** 23 | * Helper Class that provides useful Assertions. 24 | */ 25 | public final class Assert { 26 | 27 | /** 28 | * Ensures that the provided value is not {@literal null}. 29 | * 30 | * @param value to evaluate. 31 | * @param message to include in the thrown exception. 32 | * @throws IllegalArgumentException if the value is {@literal null}. 33 | */ 34 | public static void isNotNull(Object value, String message) { 35 | if (value == null) { 36 | throw new IllegalArgumentException(message); 37 | } 38 | } 39 | 40 | /** 41 | * Ensures that the provided value is not empty. Strings that are all whitespace are also 42 | * considered empty. 43 | * 44 | * @param value to evaluate. 45 | * @param message to include in the thrown exception. 46 | * @throws IllegalArgumentException if the value is {@literal null}, empty, or all whitespace. 47 | */ 48 | public static void isNotEmpty(String value, String message) { 49 | if (value == null || value.trim().isEmpty()) { 50 | throw new IllegalArgumentException(message); 51 | } 52 | } 53 | 54 | /** 55 | * Ensures that the provided collection is not empty. 56 | * 57 | * @param value to evaluate. 58 | * @param message to include in the thrown exception. 59 | * @throws IllegalArgumentException if the value is {@literal null} or empty. 60 | */ 61 | public static void isNotEmpty(Collection value, String message) { 62 | if (value == null || value.isEmpty()) { 63 | throw new IllegalArgumentException(message); 64 | } 65 | } 66 | 67 | /** 68 | * Ensures that the value provided matches the supplied expression. 69 | * 70 | * @param value to evaluate. 71 | * @param expression to use to evaluate the value. 72 | * @param message to include in the thrown exception. 73 | * @param type of the value. 74 | * @throws IllegalStateException if the value does not pass the expression. 75 | */ 76 | public static void isTrue(T value, Predicate expression, String message) { 77 | if (!expression.test(value)) { 78 | throw new IllegalStateException(message); 79 | } 80 | } 81 | 82 | /** 83 | * Ensures that the value provided does not match the supplied expression. 84 | * 85 | * @param value to evaluate. 86 | * @param expression to use to evaluate the value. 87 | * @param message to include in the thrown exception. 88 | * @param type of the value. 89 | * @throws IllegalStateException if the value does pass the expression. 90 | */ 91 | public static void isFalse(T value, Predicate expression, String message) { 92 | isTrue(value, expression.negate(), message); 93 | } 94 | 95 | } --------------------------------------------------------------------------------