├── README.md ├── feign-istio-fake ├── README.md ├── istio-fake.iml ├── pom.xml └── src │ ├── main │ └── java │ │ └── istio │ │ └── fake │ │ ├── FakeClientsRegistrar.java │ │ ├── FakeException.java │ │ ├── Util │ │ └── Util.java │ │ ├── annotation │ │ ├── EnableFakeClients.java │ │ ├── FakeClient.java │ │ ├── PathVariableParameterProcessor.java │ │ ├── QueryMapParameterProcessor.java │ │ ├── RequestHeaderParameterProcessor.java │ │ └── RequestParamParameterProcessor.java │ │ ├── base │ │ ├── Body.java │ │ ├── Client.java │ │ ├── CollectionFormat.java │ │ ├── Contract.java │ │ ├── DefaultMethodHandler.java │ │ ├── ExceptionPropagationPolicy.java │ │ ├── HeaderMap.java │ │ ├── Headers.java │ │ ├── InvocationHandlerFactory.java │ │ ├── MethodMetadata.java │ │ ├── Param.java │ │ ├── QueryMap.java │ │ ├── QueryMapEncoder.java │ │ ├── ReflectiveFake.java │ │ ├── Request.java │ │ ├── RequestInterceptor.java │ │ ├── RequestLine.java │ │ ├── RequestTemplate.java │ │ ├── Response.java │ │ ├── ResponseMapper.java │ │ ├── SynchronousMethodHandler.java │ │ ├── Types.java │ │ ├── codec │ │ │ ├── Decoder.java │ │ │ ├── Encoder.java │ │ │ ├── ErrorDecoder.java │ │ │ └── StringDecoder.java │ │ ├── log │ │ │ ├── DefaultFakeLoggerFactory.java │ │ │ ├── FakeLogger.java │ │ │ ├── FakeLoggerFactory.java │ │ │ └── Slf4JFakeLogger.java │ │ ├── querymap │ │ │ └── FieldQueryMapEncoder.java │ │ └── template │ │ │ ├── BodyTemplate.java │ │ │ ├── Expression.java │ │ │ ├── Expressions.java │ │ │ ├── HeaderTemplate.java │ │ │ ├── Literal.java │ │ │ ├── QueryTemplate.java │ │ │ ├── Template.java │ │ │ ├── TemplateChunk.java │ │ │ ├── UriTemplate.java │ │ │ └── UriUtils.java │ │ ├── configuration │ │ ├── FakeAutoConfiguration.java │ │ └── FakeClientsConfiguration.java │ │ ├── openfake │ │ ├── AnnotatedParameterProcessor.java │ │ ├── DefaultTargeter.java │ │ ├── Fake.java │ │ ├── FakeClientFactoryBean.java │ │ ├── FakeClientProperties.java │ │ ├── FakeClientSpecification.java │ │ ├── FakeContext.java │ │ ├── FakeFormatterRegistrar.java │ │ ├── OptionalDecoder.java │ │ ├── SpringQueryMap.java │ │ ├── Target.java │ │ ├── Targeter.java │ │ └── Types.java │ │ └── support │ │ ├── AbstractWriter.java │ │ ├── ByteArrayWriter.java │ │ ├── ContentProcessor.java │ │ ├── ContentType.java │ │ ├── DelegateWriter.java │ │ ├── FakeHttpClientProperties.java │ │ ├── FormData.java │ │ ├── FormDataWriter.java │ │ ├── FormEncoder.java │ │ ├── FormProperty.java │ │ ├── HttpRequestHeaderHolder.java │ │ ├── HttpRequestHeaderHolderImpl.java │ │ ├── ManyFilesWriter.java │ │ ├── ManyParametersWriter.java │ │ ├── MultipartFormContentProcessor.java │ │ ├── Output.java │ │ ├── PageJacksonModule.java │ │ ├── PageableSpringEncoder.java │ │ ├── PojoUtil.java │ │ ├── PojoWriter.java │ │ ├── ResponseEntityDecoder.java │ │ ├── SingleFileWriter.java │ │ ├── SingleParameterWriter.java │ │ ├── SpringDecoder.java │ │ ├── SpringEncoder.java │ │ ├── SpringFormEncoder.java │ │ ├── SpringManyMultipartFilesWriter.java │ │ ├── SpringMvcContract.java │ │ ├── SpringSingleMultipartFileWriter.java │ │ ├── UrlencodedFormContentProcessor.java │ │ └── Writer.java │ └── test │ └── java │ └── cn │ └── focusmedia │ └── bigdata │ └── pyramid │ └── AppTest.java ├── k8s ├── destination-rule-all.yaml ├── micro-api-canary-istio-v1.yaml ├── micro-api-canary-istio-v2.yaml ├── micro-api-canary.yaml ├── micro-api.yaml ├── micro-gateway.yaml ├── micro-order.yaml ├── micro-pay.yaml └── virtual-service-all.yaml ├── micro-api ├── docker │ └── Dockerfile ├── micro-api.iml ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── wudimanong │ │ └── micro │ │ └── api │ │ ├── MicroApi.java │ │ ├── config │ │ ├── AutoResultReturnHandler.java │ │ └── WebMvcConfig.java │ │ ├── controller │ │ ├── ApiOrderController.java │ │ └── TestController.java │ │ └── exception │ │ ├── GlobalCodeEnum.java │ │ ├── GlobalExceptionHandler.java │ │ ├── ResponseResult.java │ │ └── ServiceException.java │ └── resources │ ├── application-dev.yml │ └── application.yml ├── micro-order-client ├── micro-order-client.iml ├── pom.xml └── src │ └── main │ └── java │ └── com │ └── wudimanong │ └── micro │ └── order │ └── client │ ├── OrderServiceClient.java │ ├── bo │ └── CreateOrderBO.java │ └── dto │ ├── CreateOrderDTO.java │ └── result │ ├── GlobalCodeEnum.java │ └── ResponseResult.java ├── micro-order ├── docker │ └── Dockerfile ├── micro-order.iml ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── wudimanong │ │ └── micro │ │ └── order │ │ ├── MicroOrder.java │ │ ├── config │ │ ├── AutoResultReturnHandler.java │ │ ├── GrpcClientCommandLineRunner.java │ │ ├── GrpcClientConfiguration.java │ │ └── WebMvcConfig.java │ │ ├── controller │ │ └── OrderController.java │ │ ├── exception │ │ ├── GlobalExceptionHandler.java │ │ └── ServiceException.java │ │ └── service │ │ ├── OrderService.java │ │ └── impl │ │ └── OrderServiceImpl.java │ └── resources │ ├── application-dev.yml │ └── application.yml ├── micro-pay-client ├── micro-pay-client.iml ├── pom.xml └── src │ └── main │ └── proto │ └── paycore.proto ├── micro-pay ├── docker │ └── Dockerfile ├── pom.xml └── src │ └── main │ ├── java │ └── com │ │ └── wudimanong │ │ └── micro │ │ └── pay │ │ ├── MicroPay.java │ │ ├── config │ │ ├── GrpcCommandLineRunner.java │ │ └── GrpcServerConfiguration.java │ │ └── provider │ │ └── PayCoreProvider.java │ └── resources │ └── application.yml └── pom.xml /README.md: -------------------------------------------------------------------------------- 1 | # istio-micro-service-demo 2 | 基于Spring Boot+Istio的Service Mesh微服务架构示例代码 3 | 4 | 模拟App客户端服务调用的服务架构,其调用链路如下: 5 | 6 | micro-api(面向外部客户端的Api服务) 7 | | 8 | | http协议 9 | | 10 | micro-order(内部订单服务) 11 | | 12 | | Grpc协议 13 | | 14 | mciro-pay(内部支付服务) 15 | 16 | 如上所示链路,具体说明如下: 17 | 18 | 1)、为了完整演示在Service Mesh架构下的微服务研发过程,这里我们定义三个微服务,其中micro-api服务是面向外部客户端的接入Api服务提供Http协议访问; 19 | 20 | 2)、而micro-api与micro-order则基于微服务的注册发现机制进行内部微服务调用访问,采用Http协议; 21 | 22 | 3)、micro-order与micro-pay之间也基于微服务注册发现机制进行内部微服务调用访问,为了演示多种场景,这里两个微服务的调用采用GRpc协议; 23 | 24 | 25 | 更详细的文章说明链接: 26 | https://mp.weixin.qq.com/s/L1LoiI9NZqwZWsuCzEJN1A 27 | 28 | https://mp.weixin.qq.com/s/o2SrM7yrK9Kja2B40U63Ug -------------------------------------------------------------------------------- /feign-istio-fake/README.md: -------------------------------------------------------------------------------- 1 | 对于传统采用Spring Cloud框架构建的微服务,服务之间一般会通过FeignClient方式进行微服务调用。但在Service Mesh微服务架构下,微服务之间的通信调用则不再需要原生OpenFeign所提供的客户端负载、熔断等功能。 2 | 3 | 但是为了快速支持或迁移Spring Cloud微服务至Service Mesh体系,需要在服务调用编程方式上尽量保持原有FeignClient的调用方式,但需要去掉其中所有熔断、负载均衡相关的服务治理代码。在Service Mesh体系中这些功能将交给Istio去完成。 4 | 5 | 本项目的编写目的就是为了适应上述要求,具体功能如下: 6 | 7 | 1、支持在istio服务网格体系下,完成服务间的快速调用(体验和原先Spring Cloud Feign类似); 8 | 9 | 2、支持多环境配置,例如本地环境微服务的调用地址可配置为本地,其他环境默认为Kubernetes集群中的服务; 10 | 11 | 3、支持链路追踪,默认透传如下Header,可以自动支持jaeger、zipkin链路追踪; 12 | `"x-request-id", "x-b3-traceid", "x-b3-spanid", "x-b3-sampled", "x-b3-flags", "x-b3-parentspanid","x-ot-span-context", "x-datadog-trace-id", "x-datadog-parent-id", "x-datadog-sampled", "end-user", "user-agent" 13 | ` 14 | -------------------------------------------------------------------------------- /feign-istio-fake/istio-fake.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /feign-istio-fake/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 4.0.0 6 | 7 | istio-micro-demo 8 | com.wudimanong 9 | 1.0-SNAPSHOT 10 | 11 | 12 | istio.fake 13 | feign-istio-fake 14 | 1.0-SNAPSHOT 15 | 16 | feign-istio-fake 17 | 18 | 19 | UTF-8 20 | 1.8 21 | 1.8 22 | 23 | 24 | 25 | 26 | junit 27 | junit 28 | 4.11 29 | test 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-autoconfigure 34 | 2.1.6.RELEASE 35 | 36 | 37 | org.springframework.cloud 38 | spring-cloud-context 39 | 2.1.2.RELEASE 40 | 41 | 42 | org.slf4j 43 | slf4j-api 44 | 1.7.22 45 | 46 | 47 | org.springframework 48 | spring-web 49 | 5.1.8.RELEASE 50 | 51 | 52 | org.projectlombok 53 | lombok 54 | 1.18.8 55 | 56 | 57 | org.springframework.data 58 | spring-data-commons 59 | 2.1.9.RELEASE 60 | 61 | 62 | com.fasterxml.jackson.core 63 | jackson-databind 64 | 2.9.9 65 | 66 | 67 | javax.servlet 68 | javax.servlet-api 69 | 4.0.1 70 | provided 71 | 72 | 73 | com.alibaba 74 | fastjson 75 | 1.2.75 76 | 77 | 78 | 79 | 80 | 81 | 82 | org.apache.maven.plugins 83 | maven-source-plugin 84 | 85 | 86 | attach-sources 87 | 88 | jar 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/annotation/EnableFakeClients.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.annotation; 18 | 19 | import java.lang.annotation.Documented; 20 | import java.lang.annotation.ElementType; 21 | import java.lang.annotation.Retention; 22 | import java.lang.annotation.RetentionPolicy; 23 | import java.lang.annotation.Target; 24 | 25 | import org.springframework.boot.autoconfigure.ImportAutoConfiguration; 26 | import org.springframework.context.annotation.Import; 27 | 28 | import istio.fake.FakeClientsRegistrar; 29 | import istio.fake.configuration.FakeAutoConfiguration; 30 | import istio.fake.configuration.FakeClientsConfiguration; 31 | 32 | /** 33 | * Scans for interfaces that declare they are feign clients (via 34 | * {@link org.springframework.cloud.openfeign.FeignClient} @FeignClient). 35 | * Configures component scanning directives for use with 36 | * {@link org.springframework.context.annotation.Configuration} 37 | * @Configuration classes. 38 | * 39 | * @author Spencer Gibb 40 | * @author Dave Syer 41 | * @since 1.0 42 | */ 43 | @Retention(RetentionPolicy.RUNTIME) 44 | @Target(ElementType.TYPE) 45 | @Documented 46 | @Import(FakeClientsRegistrar.class) 47 | @ImportAutoConfiguration({FakeAutoConfiguration.class, FakeClientsConfiguration.class}) 48 | public @interface EnableFakeClients { 49 | 50 | /** 51 | * Alias for the {@link #basePackages()} attribute. Allows for more concise annotation 52 | * declarations e.g.: {@code @ComponentScan("org.my.pkg")} instead of 53 | * {@code @ComponentScan(basePackages="org.my.pkg")}. 54 | * @return the array of 'basePackages'. 55 | */ 56 | String[] value() default {}; 57 | 58 | /** 59 | * Base packages to scan for annotated components. 60 | *

61 | * {@link #value()} is an alias for (and mutually exclusive with) this attribute. 62 | *

63 | * Use {@link #basePackageClasses()} for a type-safe alternative to String-based 64 | * package names. 65 | * @return the array of 'basePackages'. 66 | */ 67 | String[] basePackages() default {}; 68 | 69 | /** 70 | * Type-safe alternative to {@link #basePackages()} for specifying the packages to 71 | * scan for annotated components. The package of each class specified will be scanned. 72 | *

73 | * Consider creating a special no-op marker class or interface in each package that 74 | * serves no purpose other than being referenced by this attribute. 75 | * @return the array of 'basePackageClasses'. 76 | */ 77 | Class[] basePackageClasses() default {}; 78 | 79 | /** 80 | * A custom @Configuration for all feign clients. Can contain override 81 | * @Bean definition for the pieces that make up the client, for instance 82 | * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}. 83 | * 84 | * @return list of default configurations 85 | */ 86 | Class[] defaultConfiguration() default {}; 87 | 88 | 89 | } 90 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/annotation/FakeClient.java: -------------------------------------------------------------------------------- 1 | package istio.fake.annotation; 2 | 3 | import java.lang.annotation.Documented; 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | import org.springframework.core.annotation.AliasFor; 10 | 11 | import istio.fake.configuration.FakeClientsConfiguration; 12 | 13 | /** 14 | * @author fandy 15 | */ 16 | @Target(ElementType.TYPE) 17 | @Retention(RetentionPolicy.RUNTIME) 18 | @Documented 19 | public @interface FakeClient { 20 | 21 | @AliasFor("name") 22 | String value() default ""; 23 | 24 | @AliasFor("value") 25 | String name() default ""; 26 | 27 | /** 28 | * A custom @Configuration for the feign client. Can contain override 29 | * @Bean definition for the pieces that make up the client, for instance 30 | * {@link feign.codec.Decoder}, {@link feign.codec.Encoder}, {@link feign.Contract}. 31 | * 32 | * @see FakeClientsConfiguration for the defaults 33 | */ 34 | Class[] configuration() default {FakeClientsConfiguration.class}; 35 | 36 | /** 37 | * @return whether to mark the feign proxy as a primary bean. Defaults to true. 38 | */ 39 | boolean primary() default true; 40 | 41 | /** 42 | * @return the @Qualifier value for the feign client. 43 | */ 44 | String qualifier() default ""; 45 | 46 | /** 47 | * @return whether 404s should be decoded instead of throwing FeignExceptions 48 | */ 49 | boolean decode404() default false; 50 | } 51 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/annotation/PathVariableParameterProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.annotation; 18 | 19 | import java.lang.annotation.Annotation; 20 | import java.lang.reflect.Method; 21 | import java.util.Collection; 22 | import java.util.Map; 23 | 24 | import org.springframework.web.bind.annotation.PathVariable; 25 | 26 | import istio.fake.util.Util; 27 | import istio.fake.base.MethodMetadata; 28 | import istio.fake.openfake.AnnotatedParameterProcessor; 29 | 30 | /** 31 | * {@link PathVariable} parameter processor. 32 | * 33 | * @author Jakub Narloch 34 | * @author Abhijit Sarkar 35 | * @see AnnotatedParameterProcessor 36 | */ 37 | public class PathVariableParameterProcessor implements AnnotatedParameterProcessor { 38 | 39 | private static final Class ANNOTATION = PathVariable.class; 40 | 41 | @Override 42 | public Class getAnnotationType() { 43 | return ANNOTATION; 44 | } 45 | 46 | @Override 47 | public boolean processArgument(AnnotatedParameterContext context, 48 | Annotation annotation, Method method) { 49 | String name = ANNOTATION.cast(annotation).value(); 50 | Util.checkState(Util.emptyToNull(name) != null, 51 | "PathVariable annotation was empty on param %s.", 52 | context.getParameterIndex()); 53 | context.setParameterName(name); 54 | 55 | MethodMetadata data = context.getMethodMetadata(); 56 | String varName = '{' + name + '}'; 57 | if (!data.template().url().contains(varName) 58 | && !searchMapValues(data.template().queries(), varName) 59 | && !searchMapValues(data.template().headers(), varName)) { 60 | data.formParams().add(name); 61 | } 62 | return true; 63 | } 64 | 65 | private boolean searchMapValues(Map> map, V search) { 66 | Collection> values = map.values(); 67 | if (values == null) { 68 | return false; 69 | } 70 | for (Collection entry : values) { 71 | if (entry.contains(search)) { 72 | return true; 73 | } 74 | } 75 | return false; 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/annotation/QueryMapParameterProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.annotation; 18 | 19 | import java.lang.annotation.Annotation; 20 | import java.lang.reflect.Method; 21 | 22 | import istio.fake.base.MethodMetadata; 23 | import istio.fake.openfake.AnnotatedParameterProcessor; 24 | import istio.fake.openfake.SpringQueryMap; 25 | 26 | /** 27 | * {@link SpringQueryMap} parameter processor. 28 | * 29 | * @author Aram Peres 30 | * @see AnnotatedParameterProcessor 31 | */ 32 | public class QueryMapParameterProcessor implements AnnotatedParameterProcessor { 33 | 34 | private static final Class ANNOTATION = SpringQueryMap.class; 35 | 36 | @Override 37 | public Class getAnnotationType() { 38 | return ANNOTATION; 39 | } 40 | 41 | @Override 42 | public boolean processArgument(AnnotatedParameterContext context, 43 | Annotation annotation, Method method) { 44 | int paramIndex = context.getParameterIndex(); 45 | MethodMetadata metadata = context.getMethodMetadata(); 46 | if (metadata.queryMapIndex() == null) { 47 | metadata.queryMapIndex(paramIndex); 48 | metadata.queryMapEncoded(SpringQueryMap.class.cast(annotation).encoded()); 49 | } 50 | return true; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/annotation/RequestHeaderParameterProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.annotation; 18 | 19 | import static istio.fake.util.Util.checkState; 20 | import static istio.fake.util.Util.emptyToNull; 21 | 22 | import java.lang.annotation.Annotation; 23 | import java.lang.reflect.Method; 24 | import java.util.Collection; 25 | import java.util.Map; 26 | 27 | import org.springframework.web.bind.annotation.RequestHeader; 28 | 29 | import istio.fake.base.MethodMetadata; 30 | import istio.fake.openfake.AnnotatedParameterProcessor; 31 | 32 | /** 33 | * {@link RequestHeader} parameter processor. 34 | * 35 | * @author Jakub Narloch 36 | * @author Abhijit Sarkar 37 | * @see AnnotatedParameterProcessor 38 | */ 39 | public class RequestHeaderParameterProcessor implements AnnotatedParameterProcessor { 40 | 41 | private static final Class ANNOTATION = RequestHeader.class; 42 | 43 | @Override 44 | public Class getAnnotationType() { 45 | return ANNOTATION; 46 | } 47 | 48 | @Override 49 | public boolean processArgument(AnnotatedParameterContext context, 50 | Annotation annotation, Method method) { 51 | int parameterIndex = context.getParameterIndex(); 52 | Class parameterType = method.getParameterTypes()[parameterIndex]; 53 | MethodMetadata data = context.getMethodMetadata(); 54 | 55 | if (Map.class.isAssignableFrom(parameterType)) { 56 | checkState(data.headerMapIndex() == null, 57 | "Header map can only be present once."); 58 | data.headerMapIndex(parameterIndex); 59 | 60 | return true; 61 | } 62 | 63 | String name = ANNOTATION.cast(annotation).value(); 64 | checkState(emptyToNull(name) != null, 65 | "RequestHeader.value() was empty on parameter %s", parameterIndex); 66 | context.setParameterName(name); 67 | 68 | Collection header = context.setTemplateParameter(name, 69 | data.template().headers().get(name)); 70 | data.template().header(name, header); 71 | return true; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/annotation/RequestParamParameterProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.annotation; 18 | 19 | import java.lang.annotation.Annotation; 20 | import java.lang.reflect.Method; 21 | import java.util.Collection; 22 | import java.util.Map; 23 | 24 | import org.springframework.web.bind.annotation.RequestParam; 25 | 26 | import istio.fake.util.Util; 27 | import istio.fake.base.MethodMetadata; 28 | import istio.fake.openfake.AnnotatedParameterProcessor; 29 | 30 | /** 31 | * {@link RequestParam} parameter processor. 32 | * 33 | * @author Jakub Narloch 34 | * @author Abhijit Sarkar 35 | * @see AnnotatedParameterProcessor 36 | */ 37 | public class RequestParamParameterProcessor implements AnnotatedParameterProcessor { 38 | 39 | private static final Class ANNOTATION = RequestParam.class; 40 | 41 | @Override 42 | public Class getAnnotationType() { 43 | return ANNOTATION; 44 | } 45 | 46 | @Override 47 | public boolean processArgument(AnnotatedParameterContext context, 48 | Annotation annotation, Method method) { 49 | int parameterIndex = context.getParameterIndex(); 50 | Class parameterType = method.getParameterTypes()[parameterIndex]; 51 | MethodMetadata data = context.getMethodMetadata(); 52 | 53 | if (Map.class.isAssignableFrom(parameterType)) { 54 | Util.checkState(data.queryMapIndex() == null, 55 | "Query map can only be present once."); 56 | data.queryMapIndex(parameterIndex); 57 | 58 | return true; 59 | } 60 | 61 | RequestParam requestParam = ANNOTATION.cast(annotation); 62 | String name = requestParam.value(); 63 | Util.checkState(Util.emptyToNull(name) != null, 64 | "RequestParam.value() was empty on parameter %s", parameterIndex); 65 | context.setParameterName(name); 66 | 67 | Collection query = context.setTemplateParameter(name, 68 | data.template().queries().get(name)); 69 | data.template().query(name, query); 70 | return true; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/Body.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import static java.lang.annotation.ElementType.METHOD; 17 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.lang.annotation.Target; 21 | import java.util.Map; 22 | 23 | /** 24 | * A possibly templated body of a PUT or POST command. variables wrapped in curly braces are 25 | * expanded before the request is submitted.
26 | * ex.
27 | * 28 | *

29 |  * @Body("<v01:getResourceRecordsOfZone><zoneName>{zoneName}</zoneName><rrType>0</rrType></v01:getResourceRecordsOfZone>")
30 |  * List<Record> listByZone(@Param("zoneName") String zoneName);
31 |  * 
32 | * 33 | *
34 | * Note that if you'd like curly braces literally in the body, urlencode them first. 35 | * 36 | * @see RequestTemplate#expand(String, Map) 37 | */ 38 | @Target(METHOD) 39 | @Retention(RUNTIME) 40 | public @interface Body { 41 | 42 | String value(); 43 | } 44 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/CollectionFormat.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import java.nio.charset.Charset; 17 | import java.util.Collection; 18 | 19 | import istio.fake.base.template.UriUtils; 20 | 21 | /** 22 | * Various ways to encode collections in URL parameters. 23 | * 24 | *

25 | * These specific cases are inspired by the OpenAPI 26 | * specification. 27 | *

28 | */ 29 | public enum CollectionFormat { 30 | /** Comma separated values, eg foo=bar,baz */ 31 | CSV(","), 32 | /** Space separated values, eg foo=bar baz */ 33 | SSV(" "), 34 | /** Tab separated values, eg foo=bar[tab]baz */ 35 | TSV("\t"), 36 | /** Values separated with the pipe (|) character, eg foo=bar|baz */ 37 | PIPES("|"), 38 | /** Parameter name repeated for each value, eg foo=bar&foo=baz */ 39 | // Using null as a special case since there is no single separator character 40 | EXPLODED(null); 41 | 42 | private final String separator; 43 | 44 | CollectionFormat(String separator) { 45 | this.separator = separator; 46 | } 47 | 48 | /** 49 | * Joins the field and possibly multiple values with the given separator. 50 | * 51 | *

52 | * Calling EXPLODED.join("foo", ["bar"]) will return "foo=bar". 53 | *

54 | * 55 | *

56 | * Calling CSV.join("foo", ["bar", "baz"]) will return "foo=bar,baz". 57 | *

58 | * 59 | *

60 | * Null values are treated somewhat specially. With EXPLODED, the field is repeated without any 61 | * "=" for backwards compatibility. With all other formats, null values are not included in the 62 | * joined value list. 63 | *

64 | * 65 | * @param field The field name corresponding to these values. 66 | * @param values A collection of value strings for the given field. 67 | * @param charset to encode the sequence 68 | * @return The formatted char sequence of the field and joined values. If the value collection is 69 | * empty, an empty char sequence will be returned. 70 | */ 71 | public CharSequence join(String field, Collection values, Charset charset) { 72 | StringBuilder builder = new StringBuilder(); 73 | int valueCount = 0; 74 | for (String value : values) { 75 | if (separator == null) { 76 | // exploded 77 | builder.append(valueCount++ == 0 ? "" : "&"); 78 | builder.append(UriUtils.queryEncode(field, charset)); 79 | if (value != null) { 80 | builder.append('='); 81 | builder.append(UriUtils.queryEncode(value, charset)); 82 | } 83 | } else { 84 | // delimited with a separator character 85 | if (builder.length() == 0) { 86 | builder.append(UriUtils.queryEncode(field, charset)); 87 | } 88 | if (value == null) { 89 | continue; 90 | } 91 | builder.append(valueCount++ == 0 ? "=" : separator); 92 | builder.append(UriUtils.queryEncode(value, charset)); 93 | } 94 | } 95 | return builder; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/DefaultMethodHandler.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import java.lang.invoke.MethodHandle; 17 | import java.lang.invoke.MethodHandles.Lookup; 18 | import java.lang.reflect.Field; 19 | import java.lang.reflect.Method; 20 | 21 | 22 | /** 23 | * Handles default methods by directly invoking the default method code on the interface. The bindTo 24 | * method must be called on the result before invoke is called. 25 | */ 26 | final class DefaultMethodHandler implements InvocationHandlerFactory.MethodHandler { 27 | // Uses Java 7 MethodHandle based reflection. As default methods will only exist when 28 | // run on a Java 8 JVM this will not affect use on legacy JVMs. 29 | // When Feign upgrades to Java 7, remove the @IgnoreJRERequirement annotation. 30 | private final MethodHandle unboundHandle; 31 | 32 | // handle is effectively final after bindTo has been called. 33 | private MethodHandle handle; 34 | 35 | public DefaultMethodHandler(Method defaultMethod) { 36 | try { 37 | Class declaringClass = defaultMethod.getDeclaringClass(); 38 | Field field = Lookup.class.getDeclaredField("IMPL_LOOKUP"); 39 | field.setAccessible(true); 40 | Lookup lookup = (Lookup) field.get(null); 41 | 42 | this.unboundHandle = lookup.unreflectSpecial(defaultMethod, declaringClass); 43 | } catch (NoSuchFieldException ex) { 44 | throw new IllegalStateException(ex); 45 | } catch (IllegalAccessException ex) { 46 | throw new IllegalStateException(ex); 47 | } 48 | } 49 | 50 | /** 51 | * Bind this handler to a proxy object. After bound, DefaultMethodHandler#invoke will act as if it 52 | * was called on the proxy object. Must be called once and only once for a given instance of 53 | * DefaultMethodHandler 54 | */ 55 | public void bindTo(Object proxy) { 56 | if (handle != null) { 57 | throw new IllegalStateException( 58 | "Attempted to rebind a default method handler that was already bound"); 59 | } 60 | handle = unboundHandle.bindTo(proxy); 61 | } 62 | 63 | /** 64 | * Invoke this method. DefaultMethodHandler#bindTo must be called before the first time invoke is 65 | * called. 66 | */ 67 | @Override 68 | public Object invoke(Object[] argv) throws Throwable { 69 | if (handle == null) { 70 | throw new IllegalStateException( 71 | "Default method handler invoked before proxy has been bound."); 72 | } 73 | return handle.invokeWithArguments(argv); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/ExceptionPropagationPolicy.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | public enum ExceptionPropagationPolicy { 17 | NONE, UNWRAP 18 | } 19 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/HeaderMap.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import static java.lang.annotation.ElementType.PARAMETER; 17 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | /** 24 | * A template parameter that can be applied to a Map that contains header entries, where the keys 25 | * are Strings that are the header field names and the values are the header field values. The 26 | * headers specified by the map will be applied to the request after all other processing, and will 27 | * take precedence over any previously specified header parameters.
28 | * This parameter is useful in cases where different header fields and values need to be set on an 29 | * API method on a per-request basis in a thread-safe manner and independently of Feign client 30 | * construction. A concrete example of a case like this are custom metadata header fields (e.g. as 31 | * "x-amz-meta-*" or "x-goog-meta-*") where the header field names are dynamic and the range of keys 32 | * cannot be determined a priori. The {@link Headers} annotation does not allow this because the 33 | * header fields that it defines are static (it is not possible to add or remove fields on a 34 | * per-request basis), and doing this using a custom {@link Target} or {@link RequestInterceptor} 35 | * can be cumbersome (it requires more code for per-method customization, it is difficult to 36 | * implement in a thread-safe manner and it requires customization when the Feign client for the API 37 | * is built).
38 | * 39 | *
40 |  * ...
41 |  * @RequestLine("GET /servers/{serverId}")
42 |  * void get(@Param("serverId") String serverId, @HeaderMap Map);
43 |  * ...
44 |  * 
45 | * 46 | * The annotated parameter must be an instance of {@link Map}, and the keys must be Strings. The 47 | * header field value of a key will be the value of its toString method, except in the following 48 | * cases:
49 | *
50 | * 57 | *
58 | * Once this conversion is applied, the query keys and resulting String values follow the same 59 | * contract as if they were set using {@link RequestTemplate#header(String, String...)}. 60 | */ 61 | @Retention(RUNTIME) 62 | @java.lang.annotation.Target(PARAMETER) 63 | public @interface HeaderMap { 64 | } 65 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/Headers.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import static java.lang.annotation.ElementType.METHOD; 17 | import static java.lang.annotation.ElementType.TYPE; 18 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 19 | 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.Target; 22 | 23 | /** 24 | * Expands headers supplied in the {@code value}. Variables to the the right of the colon are 25 | * expanded.
26 | * 27 | *
28 |  * @Headers("Content-Type: application/xml")
29 |  * interface SoapApi {
30 |  * ...   
31 |  * @RequestLine("GET /")
32 |  * @Headers("Cache-Control: max-age=640000")
33 |  * ...
34 |  *
35 |  * @RequestLine("POST /")
36 |  * @Headers({
37 |  *   "X-Foo: Bar",
38 |  *   "X-Ping: {token}"
39 |  * }) void post(@Param("token") String token);
40 |  * ...
41 |  * 
42 | * 43 | *
44 | * Notes: 45 | * 50 | *
51 | * Relationship to JAXRS
52 | *
53 | * The following two forms are identical.
54 | *
55 | * Feign: 56 | * 57 | *
58 |  * @RequestLine("POST /")
59 |  * @Headers({
60 |  *   "X-Ping: {token}"
61 |  * }) void post(@Named("token") String token);
62 |  * ...
63 |  * 
64 | * 65 | *
66 | * JAX-RS: 67 | * 68 | *
69 |  * @POST @Path("/")
70 |  * void post(@HeaderParam("X-Ping") String token);
71 |  * ...
72 |  * 
73 | */ 74 | @Target({METHOD, TYPE}) 75 | @Retention(RUNTIME) 76 | public @interface Headers { 77 | 78 | String[] value(); 79 | } 80 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/InvocationHandlerFactory.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import java.lang.reflect.InvocationHandler; 17 | import java.lang.reflect.Method; 18 | import java.util.Map; 19 | 20 | import istio.fake.openfake.Target; 21 | 22 | /** 23 | * Controls reflective method dispatch. 24 | */ 25 | public interface InvocationHandlerFactory { 26 | 27 | InvocationHandler create(Target target, Map dispatch); 28 | 29 | /** 30 | * Like {@link InvocationHandler#invoke(Object, java.lang.reflect.Method, Object[])}, except for a 31 | * single method. 32 | */ 33 | interface MethodHandler { 34 | 35 | Object invoke(Object[] argv) throws Throwable; 36 | } 37 | 38 | static final class Default implements InvocationHandlerFactory { 39 | 40 | @Override 41 | public InvocationHandler create(Target target, Map dispatch) { 42 | return new ReflectiveFake.FeignInvocationHandler(target, dispatch); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/Param.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import static java.lang.annotation.ElementType.PARAMETER; 17 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 18 | 19 | import java.lang.annotation.Retention; 20 | 21 | /** 22 | * A named template parameter applied to {@link Headers}, {@linkplain RequestLine} or 23 | * {@linkplain Body} 24 | */ 25 | @Retention(RUNTIME) 26 | @java.lang.annotation.Target(PARAMETER) 27 | public @interface Param { 28 | 29 | /** 30 | * The name of the template parameter. 31 | */ 32 | String value(); 33 | 34 | /** 35 | * How to expand the value of this parameter, if {@link ToStringExpander} isn't adequate. 36 | */ 37 | Class expander() default ToStringExpander.class; 38 | 39 | /** 40 | * Specifies whether argument is already encoded The value is ignored for headers (headers are 41 | * never encoded) 42 | * 43 | * @see QueryMap#encoded 44 | */ 45 | boolean encoded() default false; 46 | 47 | interface Expander { 48 | 49 | /** 50 | * Expands the value into a string. Does not accept or return null. 51 | */ 52 | String expand(Object value); 53 | } 54 | 55 | final class ToStringExpander implements Expander { 56 | 57 | @Override 58 | public String expand(Object value) { 59 | return value.toString(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/QueryMap.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import static java.lang.annotation.ElementType.PARAMETER; 17 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 18 | 19 | import java.lang.annotation.Retention; 20 | import java.util.List; 21 | import java.util.Map; 22 | 23 | /** 24 | * A template parameter that can be applied to a Map that contains query parameters, where the keys 25 | * are Strings that are the parameter names and the values are the parameter values. The queries 26 | * specified by the map will be applied to the request after all other processing, and will take 27 | * precedence over any previously specified query parameters. It is not necessary to reference the 28 | * parameter map as a variable.
29 | *
30 | * 31 | *
32 |  * ...
33 |  * @RequestLine("POST /servers")
34 |  * void servers(@QueryMap Map);
35 |  * ...
36 |  *
37 |  * @RequestLine("GET /servers/{serverId}?count={count}")
38 |  * void get(@Param("serverId") String serverId, @Param("count") int count, @QueryMap Map);
39 |  * ...
40 |  * 
41 | * 42 | * The annotated parameter must be an instance of {@link Map}, and the keys must be Strings. The 43 | * query value of a key will be the value of its toString method, except in the following cases: 44 | *
45 | *
46 | *
    47 | *
  • if the value is null, the value will remain null (rather than converting to the String 48 | * "null") 49 | *
  • if the value is an {@link Iterable}, it is converted to a {@link List} of String objects 50 | * where each value in the list is either null if the original value was null or the value's 51 | * toString representation otherwise. 52 | *
53 | *
54 | * Once this conversion is applied, the query keys and resulting String values follow the same 55 | * contract as if they were set using {@link RequestTemplate#query(String, String...)}. 56 | */ 57 | @Retention(RUNTIME) 58 | @java.lang.annotation.Target(PARAMETER) 59 | public @interface QueryMap { 60 | /** 61 | * Specifies whether parameter names and values are already encoded. 62 | * 63 | * @see Param#encoded 64 | */ 65 | boolean encoded() default false; 66 | } 67 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/QueryMapEncoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import java.util.Map; 17 | 18 | import istio.fake.base.querymap.FieldQueryMapEncoder; 19 | 20 | /** 21 | * A QueryMapEncoder encodes Objects into maps of query parameter names to values. 22 | * 23 | * @see FieldQueryMapEncoder 24 | * @see BeanQueryMapEncoder 25 | * 26 | */ 27 | public interface QueryMapEncoder { 28 | 29 | /** 30 | * Encodes the given object into a query map. 31 | * 32 | * @param object the object to encode 33 | * @return the map represented by the object 34 | */ 35 | Map encode(Object object); 36 | 37 | /** 38 | * @deprecated use {@link BeanQueryMapEncoder} instead. default encoder uses reflection to inspect 39 | * provided objects Fields to expand the objects values into a query string. If you 40 | * prefer that the query string be built using getter and setter methods, as defined 41 | * in the Java Beans API, please use the {@link BeanQueryMapEncoder} 42 | */ 43 | class Default extends FieldQueryMapEncoder { 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/RequestInterceptor.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | /** 17 | * Zero or more {@code RequestInterceptors} may be configured for purposes such as adding headers to 18 | * all requests. No guarantees are give with regards to the order that interceptors are applied. 19 | * Once interceptors are applied, {@link Target#apply(RequestTemplate)} is called to create the 20 | * immutable http request sent via {@link Client#execute(Request, feign.Request.Options)}.
21 | *
22 | * For example:
23 | * 24 | *

25 |  * public void apply(RequestTemplate input) {
26 |  *   input.header("X-Auth", currentToken);
27 |  * }
28 |  * 
29 | * 30 | *
31 | *
32 | * Configuration
33 | *
34 | * {@code RequestInterceptors} are configured via {@link Feign.Builder#requestInterceptors}.
35 | *
36 | * Implementation notes
37 | *
38 | * Do not add parameters, such as {@code /path/{foo}/bar } in your implementation of 39 | * {@link #apply(RequestTemplate)}.
40 | * Interceptors are applied after the template's parameters are 41 | * {@link RequestTemplate#resolve(java.util.Map) resolved}. This is to ensure that you can implement 42 | * signatures are interceptors.
43 | *
44 | *
45 | * Relationship to Retrofit 1.x
46 | *
47 | * This class is similar to {@code RequestInterceptor.intercept()}, except that the implementation 48 | * can read, remove, or otherwise mutate any part of the request template. 49 | */ 50 | public interface RequestInterceptor { 51 | 52 | /** 53 | * Called for every request. Add data using methods on the supplied {@link RequestTemplate}. 54 | */ 55 | void apply(RequestTemplate template); 56 | } 57 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/RequestLine.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import static java.lang.annotation.ElementType.METHOD; 17 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 18 | 19 | import java.lang.annotation.Retention; 20 | 21 | /** 22 | * Expands the uri template supplied in the {@code value}, permitting path and query variables, or 23 | * just the http method. Templates should conform to 24 | * RFC 6570. Support is limited to Simple String 25 | * expansion and Reserved Expansion (Level 1 and Level 2) expressions. 26 | */ 27 | @java.lang.annotation.Target(METHOD) 28 | @Retention(RUNTIME) 29 | public @interface RequestLine { 30 | 31 | String value(); 32 | 33 | boolean decodeSlash() default true; 34 | 35 | CollectionFormat collectionFormat() default CollectionFormat.EXPLODED; 36 | } 37 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/ResponseMapper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base; 15 | 16 | import java.lang.reflect.Type; 17 | 18 | /** 19 | * Map function to apply to the response before decoding it. 20 | * 21 | *
22 |  * {@code
23 |  * new ResponseMapper() {
24 |  *      @Override
25 |  *      public Response map(Response response, Type type) {
26 |  *          try {
27 |  *            return response
28 |  *              .toBuilder()
29 |  *              .body(Util.toString(response.body().asReader()).toUpperCase().getBytes())
30 |  *              .build();
31 |  *          } catch (IOException e) {
32 |  *              throw new RuntimeException(e);
33 |  *          }
34 |  *      }
35 |  *  };
36 |  * }
37 |  * 
38 | */ 39 | public interface ResponseMapper { 40 | 41 | Response map(Response response, Type type); 42 | } 43 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/codec/Decoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.codec; 15 | 16 | import java.io.IOException; 17 | import java.lang.reflect.Type; 18 | 19 | import istio.fake.FakeException; 20 | import istio.fake.util.Util; 21 | import istio.fake.base.Response; 22 | 23 | /** 24 | * Decodes an HTTP response into a single object of the given {@code type}. Invoked when 25 | * {@link Response#status()} is in the 2xx range and the return type is neither {@code void} nor 26 | * {@code 27 | * Response}. 28 | *

29 | *

30 | * Example Implementation:
31 | *

32 | * 33 | *

 34 |  * public class GsonDecoder implements Decoder {
 35 |  *   private final Gson gson = new Gson();
 36 |  *
 37 |  *   @Override
 38 |  *   public Object decode(Response response, Type type) throws IOException {
 39 |  *     try {
 40 |  *       return gson.fromJson(response.body().asReader(), type);
 41 |  *     } catch (JsonIOException e) {
 42 |  *       if (e.getCause() != null &&
 43 |  *           e.getCause() instanceof IOException) {
 44 |  *         throw IOException.class.cast(e.getCause());
 45 |  *       }
 46 |  *       throw e;
 47 |  *     }
 48 |  *   }
 49 |  * }
 50 |  * 
51 | * 52 | *
53 | *

Implementation Note

The {@code type} parameter will correspond to the 54 | * {@link java.lang.reflect.Method#getGenericReturnType() generic return type} of an 55 | * {@link feign.Target#type() interface} processed by {@link feign.Feign#newInstance(feign.Target)}. 56 | * When writing your implementation of Decoder, ensure you also test parameterized types such as 57 | * {@code 58 | * List}.
59 | *

Note on exception propagation

Exceptions thrown by {@link Decoder}s get wrapped in a 60 | * {@link DecodeException} unless they are a subclass of {@link FeignException} already, and unless 61 | * the client was configured with {@link Feign.Builder#decode404()}. 62 | */ 63 | public interface Decoder { 64 | 65 | /** 66 | * Decodes an http response into an object corresponding to its 67 | * {@link java.lang.reflect.Method#getGenericReturnType() generic return type}. If you need to 68 | * wrap exceptions, please do so via {@link DecodeException}. 69 | * 70 | * @param response the response to decode 71 | * @param type {@link java.lang.reflect.Method#getGenericReturnType() generic return type} of the 72 | * method corresponding to this {@code response}. 73 | * @return instance of {@code type} 74 | * @throws IOException will be propagated safely to the caller. 75 | * @throws DecodeException when decoding failed due to a checked exception besides IOException. 76 | * @throws FeignException when decoding succeeds, but conveys the operation failed. 77 | */ 78 | Object decode(Response response, Type type) throws IOException, FakeException; 79 | 80 | /** Default implementation of {@code Decoder}. */ 81 | public class Default extends StringDecoder { 82 | 83 | @Override 84 | public Object decode(Response response, Type type) throws IOException { 85 | if (response.status() == 404 || response.status() == 204){ 86 | return Util.emptyValueOf(type); 87 | } 88 | 89 | if (response.body() == null){ 90 | return null; 91 | } 92 | 93 | if (byte[].class.equals(type)) { 94 | return Util.toByteArray(response.body().asInputStream()); 95 | } 96 | return super.decode(response, type); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/codec/Encoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.codec; 15 | 16 | import static java.lang.String.format; 17 | 18 | import java.lang.reflect.Type; 19 | 20 | import istio.fake.FakeException; 21 | import istio.fake.util.Util; 22 | import istio.fake.base.RequestTemplate; 23 | 24 | /** 25 | * Encodes an object into an HTTP request body. Like {@code javax.websocket.Encoder}. {@code 26 | * Encoder} is used when a method parameter has no {@code @Param} annotation. For example:
27 | *

28 | * 29 | *

 30 |  * @POST
 31 |  * @Path("/")
 32 |  * void create(User user);
 33 |  * 
34 | * 35 | * Example implementation:
36 | *

37 | * 38 | *

 39 |  * public class GsonEncoder implements Encoder {
 40 |  *   private final Gson gson;
 41 |  *
 42 |  *   public GsonEncoder(Gson gson) {
 43 |  *     this.gson = gson;
 44 |  *   }
 45 |  *
 46 |  *   @Override
 47 |  *   public void encode(Object object, Type bodyType, RequestTemplate template) {
 48 |  *     template.body(gson.toJson(object, bodyType));
 49 |  *   }
 50 |  * }
 51 |  * 
52 | * 53 | *

54 | *

Form encoding

55 | *

56 | * If any parameters are found in {@link fake.MethodMetadata#formParams()}, they will be collected 57 | * and passed to the Encoder as a map. 58 | * 59 | *

60 | * Ex. The following is a form. Notice the parameters aren't consumed in the request line. A map 61 | * including "username" and "password" keys will passed to the encoder, and the body type will be 62 | * {@link #MAP_STRING_WILDCARD}. 63 | * 64 | *

 65 |  * @RequestLine("POST /")
 66 |  * Session login(@Param("username") String username, @Param("password") String password);
 67 |  * 
68 | */ 69 | public interface Encoder { 70 | /** Type literal for {@code Map}, indicating the object to encode is a form. */ 71 | Type MAP_STRING_WILDCARD = Util.MAP_STRING_WILDCARD; 72 | 73 | /** 74 | * Converts objects to an appropriate representation in the template. 75 | * 76 | * @param object what to encode as the request body. 77 | * @param bodyType the type the object should be encoded as. {@link #MAP_STRING_WILDCARD} 78 | * indicates form encoding. 79 | * @param template the request template to populate. 80 | * @throws FakeException when encoding failed due to a checked exception. 81 | */ 82 | void encode(Object object, Type bodyType, RequestTemplate template) throws FakeException; 83 | 84 | /** 85 | * Default implementation of {@code Encoder}. 86 | */ 87 | public class Default implements Encoder { 88 | 89 | @Override 90 | public void encode(Object object, Type bodyType, RequestTemplate template) { 91 | if (bodyType == String.class) { 92 | template.body(object.toString()); 93 | } else if (bodyType == byte[].class) { 94 | template.body((byte[]) object, null); 95 | } else if (object != null) { 96 | throw new FakeException( 97 | format("%s is not a type supported by this encoder.", object.getClass())); 98 | } 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/codec/StringDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | *

4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.codec; 15 | 16 | import static java.lang.String.format; 17 | 18 | import java.io.IOException; 19 | import java.lang.reflect.Type; 20 | 21 | import istio.fake.FakeException; 22 | import istio.fake.util.Util; 23 | import istio.fake.base.Response; 24 | 25 | public class StringDecoder implements Decoder { 26 | 27 | @Override 28 | public Object decode(Response response, Type type) throws IOException { 29 | Response.Body body = response.body(); 30 | if (body == null) { 31 | return null; 32 | } 33 | if (String.class.equals(type)) { 34 | return Util.toString(body.asReader()); 35 | } 36 | throw new FakeException( 37 | format("status:%d, %s is not a type supported by this decoder.", response.status(), type)); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/log/DefaultFakeLoggerFactory.java: -------------------------------------------------------------------------------- 1 | package istio.fake.base.log; 2 | 3 | public class DefaultFakeLoggerFactory implements FakeLoggerFactory { 4 | 5 | private FakeLogger fakeLogger; 6 | 7 | public DefaultFakeLoggerFactory(FakeLogger fakeLogger) { 8 | this.fakeLogger = fakeLogger; 9 | } 10 | 11 | @Override 12 | public FakeLogger create(Class type) { 13 | return this.fakeLogger != null ? this.fakeLogger : new Slf4JFakeLogger(type); 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/log/FakeLoggerFactory.java: -------------------------------------------------------------------------------- 1 | package istio.fake.base.log; 2 | 3 | public interface FakeLoggerFactory { 4 | 5 | /** 6 | * Factory method to provide a {@link FakeLogger} for a given {@link Class}. 7 | * @param type the {@link Class} for which a {@link FakeLogger} instance is to be created 8 | * @return a {@link FakeLogger} instance 9 | */ 10 | FakeLogger create(Class type); 11 | 12 | } -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/log/Slf4JFakeLogger.java: -------------------------------------------------------------------------------- 1 | package istio.fake.base.log; 2 | 3 | import java.io.IOException; 4 | 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | import istio.fake.base.Request; 9 | import istio.fake.base.Response; 10 | 11 | public class Slf4JFakeLogger extends FakeLogger { 12 | 13 | private final Logger logger; 14 | 15 | public Slf4JFakeLogger() { 16 | this(FakeLogger.class); 17 | } 18 | 19 | public Slf4JFakeLogger(Class clazz) { 20 | this(LoggerFactory.getLogger(clazz)); 21 | } 22 | 23 | public Slf4JFakeLogger(String name) { 24 | this(LoggerFactory.getLogger(name)); 25 | } 26 | 27 | Slf4JFakeLogger(Logger logger) { 28 | this.logger = logger; 29 | } 30 | 31 | @Override 32 | public void logRequest(String configKey, Level logLevel, Request request) { 33 | if (logger.isDebugEnabled()) { 34 | super.logRequest(configKey, logLevel, request); 35 | } 36 | } 37 | 38 | @Override 39 | public Response logAndRebufferResponse(String configKey, 40 | Level logLevel, 41 | Response response, 42 | long elapsedTime) 43 | throws IOException { 44 | if (logger.isDebugEnabled()) { 45 | return super.logAndRebufferResponse(configKey, logLevel, response, elapsedTime); 46 | } 47 | return response; 48 | } 49 | 50 | @Override 51 | protected void log(String configKey, String format, Object... args) { 52 | // Not using SLF4J's support for parameterized messages (even though it would be more efficient) 53 | // because it would 54 | // require the incoming message formats to be SLF4J-specific. 55 | if (logger.isDebugEnabled()) { 56 | logger.debug(String.format(methodTag(configKey) + format, args)); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/querymap/FieldQueryMapEncoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.querymap; 15 | 16 | import java.lang.reflect.Field; 17 | import java.util.Arrays; 18 | import java.util.Collections; 19 | import java.util.HashMap; 20 | import java.util.List; 21 | import java.util.Map; 22 | import java.util.stream.Collectors; 23 | 24 | import istio.fake.FakeException; 25 | import istio.fake.base.QueryMapEncoder; 26 | 27 | /** 28 | * the query map will be generated using member variable names as query parameter names. 29 | * 30 | * eg: "/uri?name={name}&number={number}" 31 | * 32 | * order of included query parameters not guaranteed, and as usual, if any value is null, it will be 33 | * left out 34 | */ 35 | public class FieldQueryMapEncoder implements QueryMapEncoder { 36 | 37 | private final Map, ObjectParamMetadata> classToMetadata = 38 | new HashMap, ObjectParamMetadata>(); 39 | 40 | @Override 41 | public Map encode(Object object) throws FakeException { 42 | try { 43 | ObjectParamMetadata metadata = getMetadata(object.getClass()); 44 | Map fieldNameToValue = new HashMap(); 45 | for (Field field : metadata.objectFields) { 46 | Object value = field.get(object); 47 | if (value != null && value != object) { 48 | fieldNameToValue.put(field.getName(), value); 49 | } 50 | } 51 | return fieldNameToValue; 52 | } catch (IllegalAccessException e) { 53 | throw new FakeException("Failure encoding object into query map", e); 54 | } 55 | } 56 | 57 | private ObjectParamMetadata getMetadata(Class objectType) { 58 | ObjectParamMetadata metadata = classToMetadata.get(objectType); 59 | if (metadata == null) { 60 | metadata = ObjectParamMetadata.parseObjectType(objectType); 61 | classToMetadata.put(objectType, metadata); 62 | } 63 | return metadata; 64 | } 65 | 66 | private static class ObjectParamMetadata { 67 | 68 | private final List objectFields; 69 | 70 | private ObjectParamMetadata(List objectFields) { 71 | this.objectFields = Collections.unmodifiableList(objectFields); 72 | } 73 | 74 | private static ObjectParamMetadata parseObjectType(Class type) { 75 | return new ObjectParamMetadata( 76 | Arrays.stream(type.getDeclaredFields()) 77 | .filter(field -> !field.isSynthetic()) 78 | .peek(field -> field.setAccessible(true)) 79 | .collect(Collectors.toList())); 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/template/BodyTemplate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.template; 15 | 16 | import java.nio.charset.Charset; 17 | import java.util.Map; 18 | 19 | import istio.fake.util.Util; 20 | 21 | /** 22 | * Template for @{@link fake.Body} annotated Templates. Unresolved expressions are preserved as 23 | * literals and literals are not URI encoded. 24 | */ 25 | public final class BodyTemplate extends Template { 26 | 27 | private static final String JSON_TOKEN_START = "{"; 28 | private static final String JSON_TOKEN_END = "}"; 29 | private static final String JSON_TOKEN_START_ENCODED = "%7B"; 30 | private static final String JSON_TOKEN_END_ENCODED = "%7D"; 31 | private boolean json = false; 32 | 33 | /** 34 | * Create a new Body Template. 35 | * 36 | * @param template to parse. 37 | * @return a Body Template instance. 38 | */ 39 | public static BodyTemplate create(String template) { 40 | return new BodyTemplate(template, Util.UTF_8); 41 | } 42 | 43 | private BodyTemplate(String value, Charset charset) { 44 | super(value, ExpansionOptions.ALLOW_UNRESOLVED, EncodingOptions.NOT_REQUIRED, false, charset); 45 | if (value.startsWith(JSON_TOKEN_START_ENCODED) && value.endsWith(JSON_TOKEN_END_ENCODED)) { 46 | this.json = true; 47 | } 48 | } 49 | 50 | @Override 51 | public String expand(Map variables) { 52 | String expanded = super.expand(variables); 53 | if (this.json) { 54 | /* decode only the first and last character */ 55 | StringBuilder sb = new StringBuilder(); 56 | sb.append(JSON_TOKEN_START); 57 | sb.append(expanded, 58 | expanded.indexOf(JSON_TOKEN_START_ENCODED) + JSON_TOKEN_START_ENCODED.length(), 59 | expanded.lastIndexOf(JSON_TOKEN_END_ENCODED)); 60 | sb.append(JSON_TOKEN_END); 61 | return sb.toString(); 62 | } 63 | return expanded; 64 | } 65 | 66 | 67 | } 68 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/template/Expression.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.template; 15 | 16 | import java.util.Optional; 17 | import java.util.regex.Pattern; 18 | 19 | /** 20 | * URI Template Expression. 21 | */ 22 | abstract class Expression implements TemplateChunk { 23 | 24 | private String name; 25 | private Pattern pattern; 26 | 27 | /** 28 | * Create a new Expression. 29 | * 30 | * @param name of the variable 31 | * @param pattern the resolved variable must adhere to, optional. 32 | */ 33 | Expression(String name, String pattern) { 34 | this.name = name; 35 | Optional.ofNullable(pattern).ifPresent(s -> this.pattern = Pattern.compile(s)); 36 | } 37 | 38 | abstract String expand(Object variable, boolean encode); 39 | 40 | public String getName() { 41 | return this.name; 42 | } 43 | 44 | Pattern getPattern() { 45 | return pattern; 46 | } 47 | 48 | /** 49 | * Checks if the provided value matches the variable pattern, if one is defined. Always true if no 50 | * pattern is defined. 51 | * 52 | * @param value to check. 53 | * @return true if it matches. 54 | */ 55 | boolean matches(String value) { 56 | if (pattern == null) { 57 | return true; 58 | } 59 | return pattern.matcher(value).matches(); 60 | } 61 | 62 | @Override 63 | public String getValue() { 64 | if (this.pattern != null) { 65 | return "{" + this.name + ":" + this.pattern + "}"; 66 | } 67 | return "{" + this.name + "}"; 68 | } 69 | 70 | @Override 71 | public String toString() { 72 | return this.getValue(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/template/HeaderTemplate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.template; 15 | 16 | import java.nio.charset.Charset; 17 | import java.util.Collection; 18 | import java.util.Collections; 19 | import java.util.Iterator; 20 | import java.util.LinkedHashSet; 21 | import java.util.Set; 22 | import java.util.stream.Collectors; 23 | import java.util.stream.StreamSupport; 24 | 25 | import istio.fake.util.Util; 26 | 27 | /** 28 | * Template for HTTP Headers. Variables that are unresolved are ignored and Literals are not 29 | * encoded. 30 | */ 31 | public final class HeaderTemplate extends Template { 32 | 33 | /* cache a copy of the variables for lookup later */ 34 | private Set values; 35 | private String name; 36 | 37 | public static HeaderTemplate create(String name, Iterable values) { 38 | if (name == null || name.isEmpty()) { 39 | throw new IllegalArgumentException("name is required."); 40 | } 41 | 42 | if (values == null) { 43 | throw new IllegalArgumentException("values are required"); 44 | } 45 | 46 | /* construct a uri template from the name and values */ 47 | StringBuilder template = new StringBuilder(); 48 | template.append(name) 49 | .append(" "); 50 | 51 | /* create a comma separated template for the header values */ 52 | Iterator iterator = values.iterator(); 53 | while (iterator.hasNext()) { 54 | template.append(iterator.next()); 55 | if (iterator.hasNext()) { 56 | template.append(", "); 57 | } 58 | } 59 | return new HeaderTemplate(template.toString(), name, values, Util.UTF_8); 60 | } 61 | 62 | /** 63 | * Append values to a Header Template. 64 | * 65 | * @param headerTemplate to append to. 66 | * @param values to append. 67 | * @return a new Header Template with the values added. 68 | */ 69 | public static HeaderTemplate append(HeaderTemplate headerTemplate, Iterable values) { 70 | Set headerValues = new LinkedHashSet<>(headerTemplate.getValues()); 71 | headerValues.addAll(StreamSupport.stream(values.spliterator(), false) 72 | .filter(Util::isNotBlank) 73 | .collect(Collectors.toSet())); 74 | return create(headerTemplate.getName(), headerValues); 75 | } 76 | 77 | /** 78 | * Creates a new Header Template. 79 | * 80 | * @param template to parse. 81 | */ 82 | private HeaderTemplate(String template, String name, Iterable values, Charset charset) { 83 | super(template, ExpansionOptions.REQUIRED, EncodingOptions.NOT_REQUIRED, false, charset); 84 | this.values = StreamSupport.stream(values.spliterator(), false) 85 | .filter(Util::isNotBlank) 86 | .collect(Collectors.toSet()); 87 | this.name = name; 88 | } 89 | 90 | public Collection getValues() { 91 | return Collections.unmodifiableCollection(values); 92 | } 93 | 94 | public String getName() { 95 | return name; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/template/Literal.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.template; 15 | 16 | /** 17 | * URI Template Literal. 18 | */ 19 | class Literal implements TemplateChunk { 20 | 21 | private final String value; 22 | 23 | /** 24 | * Create a new Literal. 25 | * 26 | * @param value of the literal. 27 | * @return the new Literal. 28 | */ 29 | public static Literal create(String value) { 30 | return new Literal(value); 31 | } 32 | 33 | /** 34 | * Create a new Literal. 35 | * 36 | * @param value of the literal. 37 | */ 38 | Literal(String value) { 39 | if (value == null || value.isEmpty()) { 40 | throw new IllegalArgumentException("a value is required."); 41 | } 42 | this.value = value; 43 | } 44 | 45 | @Override 46 | public String getValue() { 47 | return this.value; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/template/TemplateChunk.java: -------------------------------------------------------------------------------- 1 | package istio.fake.base.template; 2 | 3 | @FunctionalInterface 4 | interface TemplateChunk { 5 | 6 | String getValue(); 7 | 8 | } -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/base/template/UriTemplate.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.base.template; 15 | 16 | import java.nio.charset.Charset; 17 | 18 | /** 19 | * URI Template, as defined by RFC 6570, 20 | * supporting Level 1 expressions, 21 | * with the following differences: 22 | * 23 | *

    24 | *
  1. unresolved variables are preserved as literals
  2. 25 | *
  3. all literals are pct-encoded
  4. 26 | *
27 | */ 28 | public class UriTemplate extends Template { 29 | 30 | /** 31 | * Create a Uri Template. 32 | * 33 | * @param template representing the uri. 34 | * @param charset for encoding. 35 | * @return a new Uri Template instance. 36 | */ 37 | public static UriTemplate create(String template, Charset charset) { 38 | return new UriTemplate(template, true, charset); 39 | } 40 | 41 | /** 42 | * Create a Uri Template. 43 | * 44 | * @param template representing the uri 45 | * @param encodeSlash flag if slash characters should be encoded. 46 | * @param charset for the template. 47 | * @return a new Uri Template instance. 48 | */ 49 | public static UriTemplate create(String template, boolean encodeSlash, Charset charset) { 50 | return new UriTemplate(template, encodeSlash, charset); 51 | } 52 | 53 | /** 54 | * Append a uri fragment to the template. 55 | * 56 | * @param uriTemplate to append to. 57 | * @param fragment to append. 58 | * @return a new UriTemplate with the fragment appended. 59 | */ 60 | public static UriTemplate append(UriTemplate uriTemplate, String fragment) { 61 | return new UriTemplate(uriTemplate.toString() + fragment, uriTemplate.encodeSlash(), 62 | uriTemplate.getCharset()); 63 | } 64 | 65 | /** 66 | * Create a new Uri Template. 67 | * 68 | * @param template for the uri. 69 | * @param encodeSlash flag for encoding slash characters. 70 | * @param charset to use when encoding. 71 | */ 72 | private UriTemplate(String template, boolean encodeSlash, Charset charset) { 73 | super(template, ExpansionOptions.REQUIRED, EncodingOptions.REQUIRED, encodeSlash, charset); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/openfake/AnnotatedParameterProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.openfake; 18 | 19 | import java.lang.annotation.Annotation; 20 | import java.lang.reflect.Method; 21 | import java.util.Collection; 22 | 23 | import istio.fake.base.MethodMetadata; 24 | 25 | /** 26 | * Feign contract method parameter processor. 27 | * 28 | * @author Jakub Narloch 29 | * @author Abhijit Sarkar 30 | */ 31 | public interface AnnotatedParameterProcessor { 32 | 33 | /** 34 | * Retrieves the processor supported annotation type. 35 | * @return the annotation type 36 | */ 37 | Class getAnnotationType(); 38 | 39 | /** 40 | * Process the annotated parameter. 41 | * @param context the parameter context 42 | * @param annotation the annotation instance 43 | * @param method the method that contains the annotation 44 | * @return whether the parameter is http 45 | */ 46 | boolean processArgument(AnnotatedParameterContext context, Annotation annotation, 47 | Method method); 48 | 49 | /** 50 | * Specifies the parameter context. 51 | * 52 | * @author Jakub Narloch 53 | */ 54 | interface AnnotatedParameterContext { 55 | 56 | /** 57 | * Retrieves the method metadata. 58 | * @return the method metadata 59 | */ 60 | MethodMetadata getMethodMetadata(); 61 | 62 | /** 63 | * Retrieves the index of the parameter. 64 | * @return the parameter index 65 | */ 66 | int getParameterIndex(); 67 | 68 | /** 69 | * Sets the parameter name. 70 | * @param name the name of the parameter 71 | */ 72 | void setParameterName(String name); 73 | 74 | /** 75 | * Sets the template parameter. 76 | * @param name the template parameter 77 | * @param rest the existing parameter values 78 | * @return parameters 79 | */ 80 | Collection setTemplateParameter(String name, Collection rest); 81 | 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/openfake/DefaultTargeter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.openfake; 18 | 19 | /** 20 | * @author Spencer Gibb 21 | * @author Erik Kringen 22 | */ 23 | public class DefaultTargeter implements Targeter { 24 | 25 | @Override 26 | public T target(FakeClientFactoryBean factory, Fake.Builder fake, 27 | FakeContext context, Target.HardCodedTarget target) { 28 | 29 | return fake.target(target); 30 | } 31 | 32 | 33 | 34 | } 35 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/openfake/FakeClientSpecification.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.openfake; 18 | 19 | import java.util.Arrays; 20 | import java.util.Objects; 21 | 22 | import org.springframework.cloud.context.named.NamedContextFactory; 23 | 24 | /** 25 | * @author Dave Syer 26 | * @author Gregor Zurowski 27 | */ 28 | public class FakeClientSpecification implements NamedContextFactory.Specification { 29 | 30 | private String name; 31 | 32 | private Class[] configuration; 33 | 34 | FakeClientSpecification() { 35 | } 36 | 37 | FakeClientSpecification(String name, Class[] configuration) { 38 | this.name = name; 39 | this.configuration = configuration; 40 | } 41 | 42 | @Override 43 | public String getName() { 44 | return this.name; 45 | } 46 | 47 | public void setName(String name) { 48 | this.name = name; 49 | } 50 | 51 | @Override 52 | public Class[] getConfiguration() { 53 | return this.configuration; 54 | } 55 | 56 | public void setConfiguration(Class[] configuration) { 57 | this.configuration = configuration; 58 | } 59 | 60 | @Override 61 | public boolean equals(Object o) { 62 | if (this == o) { 63 | return true; 64 | } 65 | if (o == null || getClass() != o.getClass()) { 66 | return false; 67 | } 68 | FakeClientSpecification that = (FakeClientSpecification) o; 69 | return Objects.equals(this.name, that.name) 70 | && Arrays.equals(this.configuration, that.configuration); 71 | } 72 | 73 | @Override 74 | public int hashCode() { 75 | return Objects.hash(this.name, this.configuration); 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return new StringBuilder("FeignClientSpecification{").append("name='") 81 | .append(this.name).append("', ").append("configuration=") 82 | .append(Arrays.toString(this.configuration)).append("}").toString(); 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/openfake/FakeContext.java: -------------------------------------------------------------------------------- 1 | package istio.fake.openfake; 2 | 3 | import org.springframework.cloud.context.named.NamedContextFactory; 4 | 5 | import istio.fake.configuration.FakeClientsConfiguration; 6 | 7 | public class FakeContext extends NamedContextFactory { 8 | 9 | public FakeContext() { 10 | super(FakeClientsConfiguration.class, "fake", "fake.client.name"); 11 | } 12 | 13 | } -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/openfake/FakeFormatterRegistrar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.openfake; 18 | 19 | import org.springframework.format.FormatterRegistrar; 20 | import org.springframework.format.support.FormattingConversionService; 21 | 22 | /** 23 | * Allows an application to customize the Feign {@link FormattingConversionService}. 24 | * 25 | * @author Matt Benson 26 | */ 27 | public interface FakeFormatterRegistrar extends FormatterRegistrar { 28 | 29 | } 30 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/openfake/OptionalDecoder.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2012-2019 The Feign Authors 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 | * in compliance with the License. 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 distributed under the License 10 | * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 | * or implied. See the License for the specific language governing permissions and limitations under 12 | * the License. 13 | */ 14 | package istio.fake.openfake; 15 | 16 | import java.io.IOException; 17 | import java.lang.reflect.ParameterizedType; 18 | import java.lang.reflect.Type; 19 | import java.util.Objects; 20 | import java.util.Optional; 21 | 22 | import istio.fake.util.Util; 23 | import istio.fake.base.Response; 24 | import istio.fake.base.codec.Decoder; 25 | 26 | public final class OptionalDecoder implements Decoder { 27 | final Decoder delegate; 28 | 29 | public OptionalDecoder(Decoder delegate) { 30 | Objects.requireNonNull(delegate, "Decoder must not be null. "); 31 | this.delegate = delegate; 32 | } 33 | 34 | @Override 35 | public Object decode(Response response, Type type) throws IOException { 36 | if (!isOptional(type)) { 37 | return delegate.decode(response, type); 38 | } 39 | if (response.status() == 404 || response.status() == 204) { 40 | return Optional.empty(); 41 | } 42 | Type enclosedType = Util.resolveLastTypeParameter(type, Optional.class); 43 | return Optional.ofNullable(delegate.decode(response, enclosedType)); 44 | } 45 | 46 | static boolean isOptional(Type type) { 47 | if (!(type instanceof ParameterizedType)) { 48 | return false; 49 | } 50 | ParameterizedType parameterizedType = (ParameterizedType) type; 51 | return parameterizedType.getRawType().equals(Optional.class); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/openfake/SpringQueryMap.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.openfake; 18 | 19 | import java.lang.annotation.ElementType; 20 | import java.lang.annotation.Retention; 21 | import java.lang.annotation.RetentionPolicy; 22 | import java.lang.annotation.Target; 23 | 24 | import org.springframework.core.annotation.AliasFor; 25 | 26 | /** 27 | * Spring MVC equivalent of OpenFeign's {@link feign.QueryMap} parameter annotation. 28 | * 29 | * @author Aram Peres 30 | * @see feign.QueryMap 31 | * @see org.springframework.cloud.openfeign.annotation.QueryMapParameterProcessor 32 | */ 33 | @Retention(RetentionPolicy.RUNTIME) 34 | @Target({ ElementType.PARAMETER }) 35 | public @interface SpringQueryMap { 36 | 37 | /** 38 | * @see QueryMap#encoded() 39 | * @return alias for {@link #encoded()}. 40 | */ 41 | @AliasFor("encoded") 42 | boolean value() default false; 43 | 44 | /** 45 | * @see QueryMap#encoded() 46 | * @return Specifies whether parameter names and values are already encoded. 47 | */ 48 | @AliasFor("value") 49 | boolean encoded() default false; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/openfake/Targeter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.openfake; 18 | 19 | /** 20 | * @author Spencer Gibb 21 | */ 22 | public interface Targeter { 23 | 24 | T target(FakeClientFactoryBean factory, Fake.Builder feign, 25 | FakeContext context, Target.HardCodedTarget target); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/AbstractWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | 20 | import java.net.URLConnection; 21 | 22 | import istio.fake.FakeException; 23 | import lombok.SneakyThrows; 24 | import lombok.val; 25 | 26 | /** 27 | * 28 | * @author Artem Labazin 29 | */ 30 | public abstract class AbstractWriter implements Writer { 31 | 32 | @Override 33 | public void write (Output output, String boundary, String key, Object value) throws FakeException { 34 | output.write("--").write(boundary).write(ContentProcessor.CRLF); 35 | write(output, key, value); 36 | output.write(ContentProcessor.CRLF); 37 | } 38 | 39 | /** 40 | * Writes data for it's children. 41 | * 42 | * @param output output writer. 43 | * @param key name for piece of data. 44 | * @param value piece of data. 45 | * 46 | * @throws FakeException in case of write errors 47 | */ 48 | @SuppressWarnings({ 49 | "PMD.UncommentedEmptyMethodBody", 50 | "PMD.EmptyMethodInAbstractClassShouldBeAbstract" 51 | }) 52 | protected void write (Output output, String key, Object value) throws FakeException { 53 | } 54 | 55 | /** 56 | * Writes file's metadata. 57 | * 58 | * @param output output writer. 59 | * @param name name for piece of data. 60 | * @param fileName file name. 61 | * @param contentType type of file content. May be the {@code null}, in that case it will be determined by file name. 62 | */ 63 | @SneakyThrows 64 | protected void writeFileMetadata (Output output, String name, String fileName, String contentType) { 65 | val contentDespositionBuilder = new StringBuilder() 66 | .append("Content-Disposition: form-data; name=\"").append(name).append("\""); 67 | if (fileName != null) { 68 | contentDespositionBuilder.append("; ").append("filename=\"").append(fileName).append("\""); 69 | } 70 | 71 | String fileContentType = contentType; 72 | if (fileContentType == null) { 73 | if (fileName != null) { 74 | fileContentType = URLConnection.guessContentTypeFromName(fileName); 75 | } 76 | if (fileContentType == null) { 77 | fileContentType = "application/octet-stream"; 78 | } 79 | } 80 | 81 | val string = new StringBuilder() 82 | .append(contentDespositionBuilder.toString()).append(ContentProcessor.CRLF) 83 | .append("Content-Type: ").append(fileContentType).append(ContentProcessor.CRLF) 84 | .append("Content-Transfer-Encoding: binary").append(ContentProcessor.CRLF) 85 | .append(ContentProcessor.CRLF) 86 | .toString(); 87 | 88 | output.write(string); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/ByteArrayWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import istio.fake.FakeException; 20 | 21 | /** 22 | * 23 | * @author Artem Labazin 24 | */ 25 | public class ByteArrayWriter extends AbstractWriter { 26 | 27 | @Override 28 | public boolean isApplicable (Object value) { 29 | return value instanceof byte[]; 30 | } 31 | 32 | @Override 33 | protected void write (Output output, String key, Object value) throws FakeException { 34 | writeFileMetadata(output, key, null, null); 35 | 36 | byte[] bytes = (byte[]) value; 37 | output.write(bytes); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/ContentProcessor.java: -------------------------------------------------------------------------------- 1 | // 2 | // Source code recreated from a .class file by IntelliJ IDEA 3 | // (powered by Fernflower decompiler) 4 | // 5 | 6 | package istio.fake.support; 7 | 8 | import java.nio.charset.Charset; 9 | import java.util.Map; 10 | 11 | import istio.fake.FakeException; 12 | import istio.fake.base.RequestTemplate; 13 | 14 | public interface ContentProcessor { 15 | String CONTENT_TYPE_HEADER = "Content-Type"; 16 | String CRLF = "\r\n"; 17 | 18 | void process(RequestTemplate var1, Charset var2, Map var3) throws FakeException; 19 | 20 | ContentType getSupportedContentType(); 21 | } 22 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/ContentType.java: -------------------------------------------------------------------------------- 1 | // 2 | // Source code recreated from a .class file by IntelliJ IDEA 3 | // (powered by Fernflower decompiler) 4 | // 5 | 6 | package istio.fake.support; 7 | 8 | public enum ContentType { 9 | UNDEFINED("undefined"), 10 | URLENCODED("application/x-www-form-urlencoded"), 11 | MULTIPART("multipart/form-data"); 12 | 13 | private final String header; 14 | 15 | private ContentType(String header) { 16 | this.header = header; 17 | } 18 | 19 | public static ContentType of(String str) { 20 | if (str == null) { 21 | return UNDEFINED; 22 | } else { 23 | String trimmed = str.trim(); 24 | ContentType[] var2 = values(); 25 | int var3 = var2.length; 26 | 27 | for(int var4 = 0; var4 < var3; ++var4) { 28 | ContentType type = var2[var4]; 29 | if (trimmed.startsWith(type.getHeader())) { 30 | return type; 31 | } 32 | } 33 | 34 | return UNDEFINED; 35 | } 36 | } 37 | 38 | public String getHeader() { 39 | return this.header; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/DelegateWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static lombok.AccessLevel.PRIVATE; 20 | 21 | import istio.fake.FakeException; 22 | import istio.fake.base.RequestTemplate; 23 | import istio.fake.base.codec.Encoder; 24 | import lombok.RequiredArgsConstructor; 25 | import lombok.experimental.FieldDefaults; 26 | import lombok.val; 27 | 28 | /** 29 | * 30 | * @author Artem Labazin 31 | */ 32 | @RequiredArgsConstructor 33 | @FieldDefaults(level = PRIVATE, makeFinal = true) 34 | public class DelegateWriter extends AbstractWriter { 35 | 36 | Encoder delegate; 37 | 38 | SingleParameterWriter parameterWriter = new SingleParameterWriter(); 39 | 40 | @Override 41 | public boolean isApplicable (Object value) { 42 | return true; 43 | } 44 | 45 | @Override 46 | protected void write (Output output, String key, Object value) throws FakeException { 47 | val fake = new RequestTemplate(); 48 | delegate.encode(value, value.getClass(), fake); 49 | val bytes = fake.requestBody().asBytes(); 50 | val string = new String(bytes, output.getCharset()).replaceAll("\n", ""); 51 | parameterWriter.write(output, key, string); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/FormData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static lombok.AccessLevel.PRIVATE; 20 | 21 | import lombok.AllArgsConstructor; 22 | import lombok.Builder; 23 | import lombok.Data; 24 | import lombok.NoArgsConstructor; 25 | import lombok.experimental.FieldDefaults; 26 | 27 | /** 28 | * This object encapsulates a byte array and its associated content type. 29 | * Use if if you want to specify the content type of your provided byte array. 30 | * 31 | * @author Guillaume Simard 32 | * @since 24.03.2018 33 | */ 34 | @Data 35 | @Builder 36 | @NoArgsConstructor 37 | @AllArgsConstructor 38 | @FieldDefaults(level = PRIVATE) 39 | public class FormData { 40 | 41 | String contentType; 42 | 43 | String fileName; 44 | 45 | byte[] data; 46 | } 47 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/FormDataWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import istio.fake.FakeException; 20 | import lombok.val; 21 | 22 | /** 23 | * 24 | * @author Guillaume Simard 25 | * @since 24.03.2018 26 | */ 27 | public class FormDataWriter extends AbstractWriter { 28 | 29 | @Override 30 | public boolean isApplicable (Object value) { 31 | return value instanceof FormData; 32 | } 33 | 34 | @Override 35 | protected void write (Output output, String key, Object value) throws FakeException { 36 | val formData = (FormData) value; 37 | writeFileMetadata(output, key, formData.getFileName(), formData.getContentType()); 38 | output.write(formData.getData()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/FormProperty.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static java.lang.annotation.ElementType.FIELD; 20 | import static java.lang.annotation.RetentionPolicy.RUNTIME; 21 | 22 | import java.lang.annotation.Documented; 23 | import java.lang.annotation.Retention; 24 | import java.lang.annotation.Target; 25 | 26 | /** 27 | * 28 | * @author marembo 29 | */ 30 | @Documented 31 | @Target(FIELD) 32 | @Retention(RUNTIME) 33 | public @interface FormProperty { 34 | 35 | /** 36 | * The name of the property. 37 | */ 38 | String value (); 39 | 40 | } 41 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/HttpRequestHeaderHolder.java: -------------------------------------------------------------------------------- 1 | package istio.fake.support; 2 | 3 | import java.util.Map; 4 | 5 | import javax.servlet.ServletRequestListener; 6 | 7 | public abstract class HttpRequestHeaderHolder implements ServletRequestListener { 8 | 9 | public abstract Map getHeaderMap(); 10 | 11 | } 12 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/HttpRequestHeaderHolderImpl.java: -------------------------------------------------------------------------------- 1 | package istio.fake.support; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | import java.util.Map; 6 | 7 | import javax.servlet.ServletRequestEvent; 8 | import javax.servlet.http.HttpServletRequest; 9 | 10 | import org.springframework.util.CollectionUtils; 11 | 12 | /** 13 | * @author tanghuan@focusmedia.cn 14 | * @date 2019/7/14 15 | */ 16 | public class HttpRequestHeaderHolderImpl extends HttpRequestHeaderHolder { 17 | 18 | private List tracingHeaderList; 19 | 20 | public HttpRequestHeaderHolderImpl(List tracingHeaderList) { 21 | this.tracingHeaderList = tracingHeaderList; 22 | } 23 | 24 | private ThreadLocal httpServletRequestHolder = 25 | new InheritableThreadLocal<>(); 26 | 27 | @Override 28 | public void requestInitialized(ServletRequestEvent requestEvent) { 29 | HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest(); 30 | httpServletRequestHolder.set(request); 31 | } 32 | 33 | @Override 34 | public void requestDestroyed(ServletRequestEvent requestEvent) { 35 | httpServletRequestHolder.remove(); 36 | } 37 | 38 | public HttpServletRequest getHttpServletRequest() { 39 | return httpServletRequestHolder.get(); 40 | } 41 | 42 | @Override 43 | public Map getHeaderMap() { 44 | HttpServletRequest request = getHttpServletRequest(); 45 | if (request == null) { 46 | return null; 47 | } 48 | 49 | if (CollectionUtils.isEmpty(tracingHeaderList)) { 50 | return null; 51 | } 52 | 53 | Map headerMap = new HashMap<>(16); 54 | for (String name : tracingHeaderList) { 55 | String value = request.getHeader(name); 56 | if (value != null) { 57 | headerMap.put(name, request.getHeader(name)); 58 | } 59 | } 60 | return headerMap; 61 | 62 | } 63 | } -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/ManyFilesWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static lombok.AccessLevel.PRIVATE; 20 | 21 | import java.io.File; 22 | 23 | import istio.fake.FakeException; 24 | import lombok.experimental.FieldDefaults; 25 | import lombok.val; 26 | 27 | /** 28 | * 29 | * @author Artem Labazin 30 | */ 31 | @FieldDefaults(level = PRIVATE, makeFinal = true) 32 | public class ManyFilesWriter extends AbstractWriter { 33 | 34 | SingleFileWriter fileWriter = new SingleFileWriter(); 35 | 36 | @Override 37 | public boolean isApplicable (Object value) { 38 | if (value instanceof File[]) { 39 | return true; 40 | } 41 | if (!(value instanceof Iterable)) { 42 | return false; 43 | } 44 | val iterable = (Iterable) value; 45 | val iterator = iterable.iterator(); 46 | return iterator.hasNext() && iterator.next() instanceof File; 47 | } 48 | 49 | @Override 50 | public void write (Output output, String boundary, String key, Object value) throws FakeException { 51 | if (value instanceof File[]) { 52 | val files = (File[]) value; 53 | for (val file : files) { 54 | fileWriter.write(output, boundary, key, file); 55 | } 56 | } else if (value instanceof Iterable) { 57 | val iterable = (Iterable) value; 58 | for (val file : iterable) { 59 | fileWriter.write(output, boundary, key, file); 60 | } 61 | } else { 62 | throw new IllegalArgumentException(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/ManyParametersWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static lombok.AccessLevel.PRIVATE; 20 | 21 | import istio.fake.FakeException; 22 | import lombok.experimental.FieldDefaults; 23 | import lombok.val; 24 | 25 | /** 26 | * 27 | * @author Artem Labazin 28 | */ 29 | @FieldDefaults(level = PRIVATE, makeFinal = true) 30 | public class ManyParametersWriter extends AbstractWriter { 31 | 32 | SingleParameterWriter parameterWriter = new SingleParameterWriter(); 33 | 34 | @Override 35 | public boolean isApplicable (Object value) { 36 | if (value.getClass().isArray()) { 37 | Object[] values = (Object[]) value; 38 | return values.length > 0 && parameterWriter.isApplicable(values[0]); 39 | } 40 | if (!(value instanceof Iterable)) { 41 | return false; 42 | } 43 | val iterable = (Iterable) value; 44 | val iterator = iterable.iterator(); 45 | return iterator.hasNext() && parameterWriter.isApplicable(iterator.next()); 46 | } 47 | 48 | @Override 49 | public void write (Output output, String boundary, String key, Object value) throws FakeException { 50 | if (value.getClass().isArray()) { 51 | val objects = (Object[]) value; 52 | for (val object : objects) { 53 | parameterWriter.write(output, boundary, key, object); 54 | } 55 | } else if (value instanceof Iterable) { 56 | val iterable = (Iterable) value; 57 | for (val object : iterable) { 58 | parameterWriter.write(output, boundary, key, object); 59 | } 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/Output.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static lombok.AccessLevel.PRIVATE; 20 | 21 | import java.io.ByteArrayOutputStream; 22 | import java.io.Closeable; 23 | import java.io.IOException; 24 | import java.nio.charset.Charset; 25 | 26 | import lombok.Getter; 27 | import lombok.RequiredArgsConstructor; 28 | import lombok.SneakyThrows; 29 | import lombok.experimental.FieldDefaults; 30 | 31 | /** 32 | * Output representation utility class. 33 | * 34 | * @author Artem Labazin 35 | */ 36 | @RequiredArgsConstructor 37 | @FieldDefaults(level = PRIVATE, makeFinal = true) 38 | public class Output implements Closeable { 39 | 40 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); 41 | 42 | @Getter 43 | Charset charset; 44 | 45 | /** 46 | * Writes the string to the output. 47 | * 48 | * @param string string to write to this output 49 | * 50 | * @return this output 51 | */ 52 | public Output write (String string) { 53 | return write(string.getBytes(charset)); 54 | } 55 | 56 | /** 57 | * Writes the byte array to the output. 58 | * 59 | * @param bytes byte arrays to write to this output 60 | * 61 | * @return this output 62 | */ 63 | @SneakyThrows 64 | public Output write (byte[] bytes) { 65 | outputStream.write(bytes); 66 | return this; 67 | } 68 | 69 | /** 70 | * Writes the byte array to the output with specified offset and fixed length. 71 | * 72 | * @param bytes byte arrays to write to this output 73 | * @param offset the offset within the array of the first byte to be read. Must be non-negative and no larger than bytes.length 74 | * @param length the number of bytes to be read from the given array 75 | * 76 | * @return this output 77 | */ 78 | @SneakyThrows 79 | public Output write (byte[] bytes, int offset, int length) { 80 | outputStream.write(bytes, offset, length); 81 | return this; 82 | } 83 | 84 | /** 85 | * Returns byte array representation of this output class. 86 | * 87 | * @return byte array representation of output 88 | */ 89 | public byte[] toByteArray () { 90 | return outputStream.toByteArray(); 91 | } 92 | 93 | @Override 94 | public void close () throws IOException { 95 | outputStream.close(); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/PageableSpringEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2018 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.support; 18 | 19 | import java.lang.reflect.Type; 20 | import java.util.ArrayList; 21 | import java.util.Collection; 22 | import java.util.List; 23 | 24 | import org.springframework.data.domain.Pageable; 25 | import org.springframework.data.domain.Sort; 26 | 27 | import istio.fake.FakeException; 28 | import istio.fake.base.RequestTemplate; 29 | import istio.fake.base.codec.Encoder; 30 | 31 | 32 | /** 33 | * Provides support for encoding spring Pageable via composition. 34 | * 35 | * @author Pascal Büttiker 36 | */ 37 | public class PageableSpringEncoder implements Encoder { 38 | 39 | private final Encoder delegate; 40 | 41 | /** 42 | * Creates a new PageableSpringEncoder with the given delegate for fallback. If no 43 | * delegate is provided and this encoder cant handle the request, an EncodeException 44 | * is thrown. 45 | * @param delegate The optional delegate. 46 | */ 47 | public PageableSpringEncoder(Encoder delegate) { 48 | this.delegate = delegate; 49 | } 50 | 51 | @Override 52 | public void encode(Object object, Type bodyType, RequestTemplate template) 53 | throws FakeException { 54 | 55 | if (supports(object)) { 56 | if (object instanceof Pageable) { 57 | Pageable pageable = (Pageable) object; 58 | template.query("page", pageable.getPageNumber() + ""); 59 | template.query("size", pageable.getPageSize() + ""); 60 | if (pageable.getSort() != null) { 61 | applySort(template, pageable.getSort()); 62 | } 63 | } 64 | else if (object instanceof Sort) { 65 | Sort sort = (Sort) object; 66 | applySort(template, sort); 67 | } 68 | } 69 | else { 70 | if (delegate != null) { 71 | delegate.encode(object, bodyType, template); 72 | } 73 | else { 74 | throw new FakeException( 75 | "PageableSpringEncoder does not support the given object " 76 | + object.getClass() 77 | + " and no delegate was provided for fallback!"); 78 | } 79 | } 80 | } 81 | 82 | private void applySort(RequestTemplate template, Sort sort) { 83 | Collection existingSorts = template.queries().get("sort"); 84 | List sortQueries = existingSorts != null ? new ArrayList<>(existingSorts) 85 | : new ArrayList<>(); 86 | for (Sort.Order order : sort) { 87 | sortQueries.add(order.getProperty() + "," + order.getDirection()); 88 | } 89 | if (!sortQueries.isEmpty()) { 90 | template.query("sort", sortQueries); 91 | } 92 | } 93 | 94 | protected boolean supports(Object object) { 95 | return object instanceof Pageable || object instanceof Sort; 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/PojoUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static java.lang.reflect.Modifier.isFinal; 20 | import static java.lang.reflect.Modifier.isStatic; 21 | import static lombok.AccessLevel.PRIVATE; 22 | 23 | import java.lang.reflect.Field; 24 | import java.lang.reflect.Type; 25 | import java.rmi.UnexpectedException; 26 | import java.security.AccessController; 27 | import java.security.PrivilegedAction; 28 | import java.util.HashMap; 29 | import java.util.Map; 30 | 31 | import org.springframework.lang.Nullable; 32 | 33 | import lombok.NoArgsConstructor; 34 | import lombok.NonNull; 35 | import lombok.Setter; 36 | import lombok.SneakyThrows; 37 | import lombok.experimental.FieldDefaults; 38 | import lombok.val; 39 | 40 | /** 41 | * 42 | * @author Artem Labazin 43 | */ 44 | public final class PojoUtil { 45 | 46 | public static boolean isUserPojo (@NonNull Object object) { 47 | val type = object.getClass(); 48 | val packageName = type.getPackage().getName(); 49 | return !packageName.startsWith("java."); 50 | } 51 | 52 | public static boolean isUserPojo (@NonNull Type type) { 53 | val typeName = type.toString(); 54 | return !typeName.startsWith("class java."); 55 | } 56 | 57 | @SneakyThrows 58 | public static Map toMap (@NonNull Object object) { 59 | val result = new HashMap(); 60 | val type = object.getClass(); 61 | val setAccessibleAction = new SetAccessibleAction(); 62 | for (val field : type.getDeclaredFields()) { 63 | val modifiers = field.getModifiers(); 64 | if (isFinal(modifiers) || isStatic(modifiers)) { 65 | continue; 66 | } 67 | setAccessibleAction.setField(field); 68 | AccessController.doPrivileged(setAccessibleAction); 69 | 70 | val fieldValue = field.get(object); 71 | if (fieldValue == null) { 72 | continue; 73 | } 74 | 75 | val propertyKey = field.isAnnotationPresent(FormProperty.class) 76 | ? field.getAnnotation(FormProperty.class).value() 77 | : field.getName(); 78 | 79 | result.put(propertyKey, fieldValue); 80 | } 81 | return result; 82 | } 83 | 84 | private PojoUtil () throws UnexpectedException { 85 | throw new UnexpectedException("It is not allowed to instantiate this class"); 86 | } 87 | 88 | @Setter 89 | @NoArgsConstructor 90 | @FieldDefaults(level = PRIVATE) 91 | private static class SetAccessibleAction implements PrivilegedAction { 92 | 93 | @Nullable 94 | Field field; 95 | 96 | @Override 97 | public Object run () { 98 | field.setAccessible(true); 99 | return null; 100 | } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/PojoWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static istio.fake.support.PojoUtil.isUserPojo; 20 | import static istio.fake.support.PojoUtil.toMap; 21 | import static lombok.AccessLevel.PRIVATE; 22 | 23 | import istio.fake.FakeException; 24 | import lombok.RequiredArgsConstructor; 25 | import lombok.experimental.FieldDefaults; 26 | import lombok.val; 27 | 28 | /** 29 | * 30 | * @author Artem Labazin 31 | */ 32 | @RequiredArgsConstructor 33 | @FieldDefaults(level = PRIVATE, makeFinal = true) 34 | public class PojoWriter extends AbstractWriter { 35 | 36 | Iterable writers; 37 | 38 | @Override 39 | public boolean isApplicable (Object object) { 40 | return isUserPojo(object); 41 | } 42 | 43 | @Override 44 | public void write (Output output, String boundary, String key, Object object) throws FakeException { 45 | val map = toMap(object); 46 | for (val entry : map.entrySet()) { 47 | val writer = findApplicableWriter(entry.getValue()); 48 | if (writer == null) { 49 | continue; 50 | } 51 | 52 | writer.write(output, boundary, entry.getKey(), entry.getValue()); 53 | } 54 | } 55 | 56 | private Writer findApplicableWriter (Object value) { 57 | for (val writer : writers) { 58 | if (writer.isApplicable(value)) { 59 | return writer; 60 | } 61 | } 62 | return null; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/ResponseEntityDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.support; 18 | 19 | import java.io.IOException; 20 | import java.lang.reflect.ParameterizedType; 21 | import java.lang.reflect.Type; 22 | import java.util.LinkedList; 23 | 24 | import org.springframework.http.HttpEntity; 25 | import org.springframework.http.HttpStatus; 26 | import org.springframework.http.ResponseEntity; 27 | import org.springframework.util.LinkedMultiValueMap; 28 | import org.springframework.util.MultiValueMap; 29 | 30 | import istio.fake.FakeException; 31 | import istio.fake.base.Response; 32 | import istio.fake.base.codec.Decoder; 33 | 34 | /** 35 | * Decoder adds compatibility for Spring MVC's ResponseEntity to any other decoder via 36 | * composition. 37 | * 38 | * @author chad jaros 39 | */ 40 | public class ResponseEntityDecoder implements Decoder { 41 | 42 | private Decoder decoder; 43 | 44 | public ResponseEntityDecoder(Decoder decoder) { 45 | this.decoder = decoder; 46 | } 47 | 48 | @Override 49 | public Object decode(final Response response, Type type) 50 | throws IOException, FakeException { 51 | 52 | if (isParameterizeHttpEntity(type)) { 53 | type = ((ParameterizedType) type).getActualTypeArguments()[0]; 54 | Object decodedObject = this.decoder.decode(response, type); 55 | 56 | return createResponse(decodedObject, response); 57 | } 58 | else if (isHttpEntity(type)) { 59 | return createResponse(null, response); 60 | } 61 | else { 62 | return this.decoder.decode(response, type); 63 | } 64 | } 65 | 66 | private boolean isParameterizeHttpEntity(Type type) { 67 | if (type instanceof ParameterizedType) { 68 | return isHttpEntity(((ParameterizedType) type).getRawType()); 69 | } 70 | return false; 71 | } 72 | 73 | private boolean isHttpEntity(Type type) { 74 | if (type instanceof Class) { 75 | Class c = (Class) type; 76 | return HttpEntity.class.isAssignableFrom(c); 77 | } 78 | return false; 79 | } 80 | 81 | @SuppressWarnings("unchecked") 82 | private ResponseEntity createResponse(Object instance, Response response) { 83 | 84 | MultiValueMap headers = new LinkedMultiValueMap<>(); 85 | for (String key : response.headers().keySet()) { 86 | headers.put(key, new LinkedList<>(response.headers().get(key))); 87 | } 88 | 89 | return new ResponseEntity<>((T) instance, headers, 90 | HttpStatus.valueOf(response.status())); 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/SingleFileWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import java.io.File; 20 | import java.io.FileInputStream; 21 | import java.io.IOException; 22 | import java.io.InputStream; 23 | 24 | import istio.fake.FakeException; 25 | import lombok.extern.slf4j.Slf4j; 26 | import lombok.val; 27 | 28 | /** 29 | * 30 | * @author Artem Labazin 31 | */ 32 | @Slf4j 33 | public class SingleFileWriter extends AbstractWriter { 34 | 35 | @Override 36 | public boolean isApplicable (Object value) { 37 | return value instanceof File; 38 | } 39 | 40 | @Override 41 | protected void write (Output output, String key, Object value) throws FakeException { 42 | val file = (File) value; 43 | writeFileMetadata(output, key, file.getName(), null); 44 | 45 | InputStream input = null; 46 | try { 47 | input = new FileInputStream(file); 48 | val buf = new byte[4096]; 49 | int length = input.read(buf); 50 | while (length > 0) { 51 | output.write(buf, 0, length); 52 | length = input.read(buf); 53 | } 54 | } catch (IOException ex) { 55 | val message = String.format("Writing file's '%s' content error", file.getName()); 56 | throw new FakeException(message, ex); 57 | } finally { 58 | if (input != null) { 59 | try { 60 | input.close(); 61 | } catch (IOException ex) { 62 | log.error("Closing file '{}' error", file.getName(), ex); 63 | } 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/SingleParameterWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import istio.fake.FakeException; 20 | import lombok.val; 21 | 22 | /** 23 | * 24 | * @author Artem Labazin 25 | */ 26 | public class SingleParameterWriter extends AbstractWriter { 27 | 28 | @Override 29 | public boolean isApplicable (Object value) { 30 | return value instanceof Number || 31 | value instanceof CharSequence || 32 | value instanceof Boolean; 33 | } 34 | 35 | @Override 36 | protected void write (Output output, String key, Object value) throws FakeException { 37 | val string = new StringBuilder() 38 | .append("Content-Disposition: form-data; name=\"").append(key).append('"').append(ContentProcessor.CRLF) 39 | .append("Content-Type: text/plain; charset=").append(output.getCharset().name()).append(ContentProcessor.CRLF) 40 | .append(ContentProcessor.CRLF) 41 | .append(value.toString()) 42 | .toString(); 43 | 44 | output.write(string); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/SpringDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package istio.fake.support; 18 | 19 | import java.io.IOException; 20 | import java.io.InputStream; 21 | import java.lang.reflect.ParameterizedType; 22 | import java.lang.reflect.Type; 23 | import java.lang.reflect.WildcardType; 24 | 25 | import org.springframework.beans.factory.ObjectFactory; 26 | import org.springframework.boot.autoconfigure.http.HttpMessageConverters; 27 | import org.springframework.http.HttpHeaders; 28 | import org.springframework.http.HttpStatus; 29 | import org.springframework.http.client.ClientHttpResponse; 30 | import org.springframework.web.client.HttpMessageConverterExtractor; 31 | 32 | import istio.fake.FakeException; 33 | import istio.fake.util.Util; 34 | import istio.fake.base.Response; 35 | import istio.fake.base.codec.Decoder; 36 | 37 | /** 38 | * @author Spencer Gibb 39 | */ 40 | public class SpringDecoder implements Decoder { 41 | 42 | private ObjectFactory messageConverters; 43 | 44 | public SpringDecoder(ObjectFactory messageConverters) { 45 | this.messageConverters = messageConverters; 46 | } 47 | 48 | @Override 49 | public Object decode(final Response response, Type type) 50 | throws IOException, FakeException { 51 | if (type instanceof Class || type instanceof ParameterizedType 52 | || type instanceof WildcardType) { 53 | @SuppressWarnings({ "unchecked", "rawtypes" }) 54 | HttpMessageConverterExtractor extractor = new HttpMessageConverterExtractor( 55 | type, this.messageConverters.getObject().getConverters()); 56 | 57 | return extractor.extractData(new FeignResponseAdapter(response)); 58 | } 59 | throw new FakeException(response.status(), 60 | "type is not an instance of Class or ParameterizedType: " + type); 61 | } 62 | 63 | private final class FeignResponseAdapter implements ClientHttpResponse { 64 | 65 | private final Response response; 66 | 67 | private FeignResponseAdapter(Response response) { 68 | this.response = response; 69 | } 70 | 71 | @Override 72 | public HttpStatus getStatusCode() throws IOException { 73 | return HttpStatus.valueOf(this.response.status()); 74 | } 75 | 76 | @Override 77 | public int getRawStatusCode() throws IOException { 78 | return this.response.status(); 79 | } 80 | 81 | @Override 82 | public String getStatusText() throws IOException { 83 | return this.response.reason(); 84 | } 85 | 86 | @Override 87 | public void close() { 88 | try { 89 | this.response.body().close(); 90 | } 91 | catch (IOException ex) { 92 | // Ignore exception on close... 93 | } 94 | } 95 | 96 | @Override 97 | public InputStream getBody() throws IOException { 98 | return this.response.body().asInputStream(); 99 | } 100 | 101 | @Override 102 | public HttpHeaders getHeaders() { 103 | return Util.getHttpHeaders(this.response.headers()); 104 | } 105 | 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/SpringFormEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static java.util.Collections.singletonMap; 20 | 21 | import java.lang.reflect.Type; 22 | import java.util.HashMap; 23 | 24 | import org.springframework.web.multipart.MultipartFile; 25 | 26 | import istio.fake.FakeException; 27 | import istio.fake.base.RequestTemplate; 28 | import istio.fake.base.codec.Encoder; 29 | import lombok.val; 30 | 31 | /** 32 | * Adds support for {@link MultipartFile} type to {@link FormEncoder}. 33 | * 34 | * @author Tomasz Juchniewicz <tjuchniewicz@gmail.com> 35 | * @since 14.09.2016 36 | */ 37 | public class SpringFormEncoder extends FormEncoder { 38 | 39 | /** 40 | * Constructor with the default Feign's encoder as a delegate. 41 | */ 42 | public SpringFormEncoder () { 43 | this(new Encoder.Default()); 44 | } 45 | 46 | /** 47 | * Constructor with specified delegate encoder. 48 | * 49 | * @param delegate delegate encoder, if this encoder couldn't encode object. 50 | */ 51 | public SpringFormEncoder (Encoder delegate) { 52 | super(delegate); 53 | 54 | val processor = (MultipartFormContentProcessor) getContentProcessor(ContentType.MULTIPART); 55 | processor.addFirstWriter(new SpringSingleMultipartFileWriter()); 56 | processor.addFirstWriter(new SpringManyMultipartFilesWriter()); 57 | } 58 | 59 | @Override 60 | public void encode (Object object, Type bodyType, RequestTemplate template) throws FakeException { 61 | if (bodyType.equals(MultipartFile[].class)) { 62 | val files = (MultipartFile[]) object; 63 | val data = new HashMap(files.length, 1.F); 64 | for (val file : files) { 65 | data.put(file.getName(), file); 66 | } 67 | super.encode(data, MAP_STRING_WILDCARD, template); 68 | } else if (bodyType.equals(MultipartFile.class)) { 69 | val file = (MultipartFile) object; 70 | val data = singletonMap(file.getName(), object); 71 | super.encode(data, MAP_STRING_WILDCARD, template); 72 | } else if (isMultipartFileCollection(object)) { 73 | val iterable = (Iterable) object; 74 | val data = new HashMap(); 75 | for (val item : iterable) { 76 | val file = (MultipartFile) item; 77 | data.put(file.getName(), file); 78 | } 79 | super.encode(data, MAP_STRING_WILDCARD, template); 80 | } else { 81 | super.encode(object, bodyType, template); 82 | } 83 | } 84 | 85 | private boolean isMultipartFileCollection (Object object) { 86 | if (!(object instanceof Iterable)) { 87 | return false; 88 | } 89 | val iterable = (Iterable) object; 90 | val iterator = iterable.iterator(); 91 | return iterator.hasNext() && iterator.next() instanceof MultipartFile; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/SpringManyMultipartFilesWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import static lombok.AccessLevel.PRIVATE; 20 | 21 | import org.springframework.web.multipart.MultipartFile; 22 | 23 | import istio.fake.FakeException; 24 | import lombok.experimental.FieldDefaults; 25 | import lombok.val; 26 | 27 | /** 28 | * 29 | * @author Artem Labazin 30 | */ 31 | @FieldDefaults(level = PRIVATE, makeFinal = true) 32 | public class SpringManyMultipartFilesWriter extends AbstractWriter { 33 | 34 | SpringSingleMultipartFileWriter fileWriter = new SpringSingleMultipartFileWriter(); 35 | 36 | @Override 37 | public boolean isApplicable (Object value) { 38 | if (value instanceof MultipartFile[]) { 39 | return true; 40 | } 41 | if (!(value instanceof Iterable)) { 42 | return false; 43 | } 44 | val iterable = (Iterable) value; 45 | val iterator = iterable.iterator(); 46 | return iterator.hasNext() && iterator.next() instanceof MultipartFile; 47 | } 48 | 49 | @Override 50 | public void write (Output output, String boundary, String key, Object value) throws FakeException { 51 | if (value instanceof MultipartFile[]) { 52 | val files = (MultipartFile[]) value; 53 | for (val file : files) { 54 | fileWriter.write(output, boundary, key, file); 55 | } 56 | } else if (value instanceof Iterable) { 57 | val iterable = (Iterable) value; 58 | for (val file : iterable) { 59 | fileWriter.write(output, boundary, key, file); 60 | } 61 | } else { 62 | throw new IllegalArgumentException(); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/SpringSingleMultipartFileWriter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | import java.io.IOException; 20 | 21 | import org.springframework.web.multipart.MultipartFile; 22 | 23 | import istio.fake.FakeException; 24 | import lombok.val; 25 | 26 | /** 27 | * 28 | * @author Artem Labazin 29 | */ 30 | public class SpringSingleMultipartFileWriter extends AbstractWriter { 31 | 32 | @Override 33 | public boolean isApplicable (Object value) { 34 | return value instanceof MultipartFile; 35 | } 36 | 37 | @Override 38 | protected void write (Output output, String key, Object value) throws FakeException { 39 | val file = (MultipartFile) value; 40 | writeFileMetadata(output, key, file.getOriginalFilename(), file.getContentType()); 41 | 42 | byte[] bytes; 43 | try { 44 | bytes = file.getBytes(); 45 | } catch (IOException ex) { 46 | throw new FakeException("Getting multipart file's content bytes error", ex); 47 | } 48 | output.write(bytes); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/UrlencodedFormContentProcessor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | 20 | import static istio.fake.support.ContentType.URLENCODED; 21 | 22 | import java.net.URLEncoder; 23 | import java.nio.charset.Charset; 24 | import java.util.Collection; 25 | import java.util.Collections; 26 | import java.util.Map; 27 | import java.util.Map.Entry; 28 | 29 | import istio.fake.FakeException; 30 | import istio.fake.base.Request; 31 | import istio.fake.base.RequestTemplate; 32 | import lombok.SneakyThrows; 33 | import lombok.val; 34 | 35 | /** 36 | * 37 | * @author Artem Labazin 38 | */ 39 | public class UrlencodedFormContentProcessor implements ContentProcessor { 40 | 41 | private static final char QUERY_DELIMITER = '&'; 42 | 43 | private static final char EQUAL_SIGN = '='; 44 | 45 | @SneakyThrows 46 | private static String encode (Object string, Charset charset) { 47 | return URLEncoder.encode(string.toString(), charset.name()); 48 | } 49 | 50 | @Override 51 | public void process (RequestTemplate template, Charset charset, Map data) throws FakeException { 52 | val bodyData = new StringBuilder(); 53 | for (Entry entry : data.entrySet()) { 54 | if (entry == null || entry.getKey() == null) { 55 | continue; 56 | } 57 | if (bodyData.length() > 0) { 58 | bodyData.append(QUERY_DELIMITER); 59 | } 60 | bodyData.append(createKeyValuePair(entry, charset)); 61 | } 62 | 63 | val contentTypeValue = new StringBuilder() 64 | .append(getSupportedContentType().getHeader()) 65 | .append("; charset=").append(charset.name()) 66 | .toString(); 67 | 68 | val bytes = bodyData.toString().getBytes(charset); 69 | val body = Request.Body.encoded(bytes, charset); 70 | 71 | template.header(CONTENT_TYPE_HEADER, Collections.emptyList()); // reset header 72 | template.header(CONTENT_TYPE_HEADER, contentTypeValue); 73 | template.body(body); 74 | } 75 | 76 | @Override 77 | public ContentType getSupportedContentType () { 78 | return URLENCODED; 79 | } 80 | 81 | private String createKeyValuePair (Entry entry, Charset charset) { 82 | String encodedKey = encode(entry.getKey(), charset); 83 | Object value = entry.getValue(); 84 | 85 | if (value == null) { 86 | return encodedKey; 87 | } else if (value.getClass().isArray()) { 88 | return createKeyValuePairFromArray(encodedKey, value, charset); 89 | } else if (value instanceof Collection) { 90 | return createKeyValuePairFromCollection(encodedKey, value, charset); 91 | } 92 | return new StringBuilder() 93 | .append(encodedKey) 94 | .append(EQUAL_SIGN) 95 | .append(encode(value, charset)) 96 | .toString(); 97 | } 98 | 99 | @SuppressWarnings("unchecked") 100 | private String createKeyValuePairFromCollection (String key, Object values, Charset charset) { 101 | val collection = (Collection) values; 102 | val array = collection.toArray(new Object[0]); 103 | return createKeyValuePairFromArray(key, array, charset); 104 | } 105 | 106 | private String createKeyValuePairFromArray (String key, Object values, Charset charset) { 107 | val result = new StringBuilder(); 108 | val array = (Object[]) values; 109 | 110 | for (int index = 0; index < array.length; index++) { 111 | val value = array[index]; 112 | if (value == null) { 113 | continue; 114 | } 115 | 116 | if (index > 0) { 117 | result.append(QUERY_DELIMITER); 118 | } 119 | 120 | result 121 | .append(key) 122 | .append(EQUAL_SIGN) 123 | .append(encode(value, charset)); 124 | } 125 | return result.toString(); 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /feign-istio-fake/src/main/java/istio/fake/support/Writer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * 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 istio.fake.support; 18 | 19 | 20 | import istio.fake.FakeException; 21 | 22 | /** 23 | * 24 | * @author Artem Labazin 25 | */ 26 | public interface Writer { 27 | 28 | /** 29 | * Processing form data to request body. 30 | * 31 | * @param output output writer. 32 | * @param boundary data boundary. 33 | * @param key name for piece of data. 34 | * @param value piece of data. 35 | * 36 | * @throws FakeException in case of any encode exception 37 | */ 38 | void write (Output output, String boundary, String key, Object value) throws FakeException; 39 | 40 | /** 41 | * Answers on question - "could this writer properly write the value". 42 | * 43 | * @param value object to write. 44 | * 45 | * @return {@code true} - if could write this object, otherwise {@code true} 46 | */ 47 | boolean isApplicable (Object value); 48 | } 49 | -------------------------------------------------------------------------------- /feign-istio-fake/src/test/java/cn/focusmedia/bigdata/pyramid/AppTest.java: -------------------------------------------------------------------------------- 1 | package cn.focusmedia.bigdata.pyramid; 2 | 3 | import static org.junit.Assert.assertTrue; 4 | 5 | import org.junit.Test; 6 | 7 | /** 8 | * Unit test for simple App. 9 | */ 10 | public class AppTest 11 | { 12 | /** 13 | * Rigorous Test :-) 14 | */ 15 | @Test 16 | public void shouldAnswerWithTrue() 17 | { 18 | assertTrue( true ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /k8s/destination-rule-all.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: networking.istio.io/v1alpha3 3 | kind: DestinationRule 4 | metadata: 5 | name: micro-api-destination 6 | spec: 7 | host: micro-api 8 | #流量策略设置:负载均衡策略、连接池大小、局部异常检测等,在路由发生后作用于流量 9 | trafficPolicy: 10 | #限流策略 11 | connectionPool: 12 | tcp: 13 | maxConnections: 10 14 | http: 15 | http1MaxPendingRequests: 1 16 | maxRequestsPerConnection: 1 17 | #设置目的地的负债均衡算法 18 | loadBalancer: 19 | simple: ROUND_ROBIN 20 | #目的地指的是不同的子集(subset)或服务版本。通子集(subset),可以识别应用程序的不同版本,以实现流量在不同服务版本之间的切换 21 | subsets: 22 | - name: v1 23 | labels: 24 | version: v1 25 | - name: v2 26 | labels: 27 | version: v2 28 | 29 | --- 30 | 31 | apiVersion: networking.istio.io/v1alpha3 32 | kind: DestinationRule 33 | metadata: 34 | name: micro-pay 35 | spec: 36 | host: micro-pay 37 | trafficPolicy: 38 | #限流策略 39 | connectionPool: 40 | tcp: 41 | maxConnections: 1 42 | http: 43 | #http2MaxRequests: 1 44 | http1MaxPendingRequests: 1 45 | maxRequestsPerConnection: 1 46 | #熔断策略 47 | outlierDetection: 48 | consecutive5xxErrors: 1 49 | interval: 30s 50 | baseEjectionTime: 3m 51 | maxEjectionPercent: 100 52 | --- -------------------------------------------------------------------------------- /k8s/micro-api-canary-istio-v1.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: micro-api 5 | spec: 6 | type: ClusterIP 7 | ports: 8 | - name: http 9 | port: 19090 10 | targetPort: 9090 11 | selector: 12 | app: micro-api 13 | 14 | --- 15 | 16 | apiVersion: apps/v1 17 | kind: Deployment 18 | metadata: 19 | name: micro-api-v1 20 | spec: 21 | selector: 22 | matchLabels: 23 | app: micro-api 24 | #这里是关键,需要设置版本标签,以便实现灰度发布 25 | version: v1 26 | replicas: 3 27 | #设置滚动升级策略 28 | #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 29 | minReadySeconds: 5 30 | strategy: 31 | type: RollingUpdate 32 | rollingUpdate: 33 | #升级过程中最多可以比原先设置多出的Pod数量 34 | maxSurge: 1 35 | #升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间 36 | maxUnavailable: 1 37 | template: 38 | metadata: 39 | labels: 40 | app: micro-api 41 | #设置版本标签,便于灰度发布 42 | version: v1 43 | spec: 44 | #设置的阿里云私有镜像仓库登陆信息的secret 45 | imagePullSecrets: 46 | - name: regcred 47 | containers: 48 | - name: micro-api 49 | image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.1-SNAPSHOT 50 | imagePullPolicy: Always 51 | tty: true 52 | ports: 53 | - name: http 54 | protocol: TCP 55 | containerPort: 19090 56 | -------------------------------------------------------------------------------- /k8s/micro-api-canary-istio-v2.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: micro-api-v2 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: micro-api 9 | #设置好版本标签,便于灰度发布 10 | version: v2 11 | replicas: 3 12 | #设置滚动升级策略 13 | #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 14 | minReadySeconds: 5 15 | strategy: 16 | type: RollingUpdate 17 | rollingUpdate: 18 | #升级过程中最多可以比原先设置多出的Pod数量 19 | maxSurge: 1 20 | #升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间 21 | maxUnavailable: 1 22 | template: 23 | metadata: 24 | labels: 25 | app: micro-api 26 | #设置好版本标签,便于灰度发布 27 | version: v2 28 | spec: 29 | #设置的阿里云私有镜像仓库登陆信息的secret 30 | imagePullSecrets: 31 | - name: regcred 32 | containers: 33 | - name: micro-api 34 | image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.3-SNAPSHOT 35 | imagePullPolicy: Always 36 | tty: true 37 | ports: 38 | - name: http 39 | protocol: TCP 40 | containerPort: 19090 41 | -------------------------------------------------------------------------------- /k8s/micro-api-canary.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apps/v1 2 | kind: Deployment 3 | metadata: 4 | name: micro-api 5 | spec: 6 | selector: 7 | matchLabels: 8 | app: micro-api 9 | replicas: 3 10 | #设置滚动升级策略 11 | #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 12 | minReadySeconds: 5 13 | strategy: 14 | type: RollingUpdate 15 | rollingUpdate: 16 | #升级过程中最多可以比原先设置多出的Pod数量 17 | maxSurge: 1 18 | #升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间 19 | maxUnavailable: 1 20 | template: 21 | metadata: 22 | labels: 23 | app: micro-api 24 | #增加新的标签(演示k8s的灰度发布) 25 | track: canary 26 | spec: 27 | #设置的阿里云私有镜像仓库登陆信息的secret(对应2.1.2的设置) 28 | imagePullSecrets: 29 | - name: regcred 30 | containers: 31 | - name: micro-api 32 | image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.3-SNAPSHOT 33 | imagePullPolicy: Always 34 | tty: true 35 | ports: 36 | - name: http 37 | protocol: TCP 38 | containerPort: 19090 39 | -------------------------------------------------------------------------------- /k8s/micro-api.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: micro-api 5 | spec: 6 | type: ClusterIP 7 | ports: 8 | - name: http 9 | port: 19090 10 | targetPort: 9090 11 | selector: 12 | app: micro-api 13 | 14 | --- 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: micro-api 19 | spec: 20 | selector: 21 | matchLabels: 22 | app: micro-api 23 | replicas: 3 24 | #设置滚动升级策略 25 | #Kubernetes在等待设置的时间后才开始进行升级,例如5秒 26 | minReadySeconds: 5 27 | strategy: 28 | type: RollingUpdate 29 | rollingUpdate: 30 | #升级过程中最多可以比原先设置多出的Pod数量 31 | maxSurge: 1 32 | #升级过程中Deployment控制器最多可以删除多少个旧Pod,主要用于提供缓冲时间 33 | maxUnavailable: 1 34 | template: 35 | metadata: 36 | labels: 37 | app: micro-api 38 | spec: 39 | #设置的阿里云私有镜像仓库登陆信息的secret(对应2.1.2的设置) 40 | imagePullSecrets: 41 | - name: regcred 42 | containers: 43 | - name: micro-api 44 | image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-api:1.0-SNAPSHOT 45 | imagePullPolicy: Always 46 | tty: true 47 | ports: 48 | - name: http 49 | protocol: TCP 50 | containerPort: 19090 51 | -------------------------------------------------------------------------------- /k8s/micro-gateway.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: Gateway 3 | metadata: 4 | name: micro-gateway 5 | spec: 6 | selector: 7 | istio: ingressgateway 8 | servers: 9 | - port: 10 | number: 80 11 | name: http 12 | protocol: HTTP 13 | hosts: 14 | - "*" -------------------------------------------------------------------------------- /k8s/micro-order.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: micro-order 5 | labels: 6 | app: micro-order 7 | service: micro-order 8 | spec: 9 | type: ClusterIP 10 | ports: 11 | - name: http 12 | #此处设置80端口的原因在于改造的Mock FeignClient代码默认是基于80端口进行服务调用 13 | port: 80 14 | targetPort: 9091 15 | selector: 16 | app: micro-order 17 | 18 | --- 19 | apiVersion: apps/v1 20 | kind: Deployment 21 | metadata: 22 | name: micro-order-v1 23 | labels: 24 | app: micro-order 25 | version: v1 26 | spec: 27 | replicas: 1 28 | selector: 29 | matchLabels: 30 | app: micro-order 31 | version: v1 32 | template: 33 | metadata: 34 | labels: 35 | app: micro-order 36 | version: v1 37 | spec: 38 | #设置的阿里云私有镜像仓库登陆信息的secret(对应2.1.2的设置) 39 | imagePullSecrets: 40 | - name: regcred 41 | containers: 42 | - name: micro-order 43 | image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-order:1.0-SNAPSHOT 44 | imagePullPolicy: Always 45 | tty: true 46 | ports: 47 | - name: http 48 | protocol: TCP 49 | containerPort: 19091 50 | #环境参数设置(设置微服务返回gRPC服务端的地址+端口) 51 | env: 52 | - name: GRPC_SERVER_HOST 53 | value: micro-pay 54 | - name: GRPC_SERVER_PORT 55 | value: "18888" 56 | 57 | 58 | -------------------------------------------------------------------------------- /k8s/micro-pay.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: v1 2 | kind: Service 3 | metadata: 4 | name: micro-pay 5 | labels: 6 | app: micro-pay 7 | service: micro-pay 8 | spec: 9 | type: ClusterIP 10 | ports: 11 | - name: http 12 | #容器暴露端口 13 | port: 19092 14 | #目标应用端口 15 | targetPort: 9092 16 | #设置gRPC端口 17 | - name: grpc 18 | port: 18888 19 | targetPort: 18888 20 | selector: 21 | app: micro-pay 22 | 23 | --- 24 | apiVersion: apps/v1 25 | kind: Deployment 26 | metadata: 27 | name: micro-pay-v1 28 | labels: 29 | app: micro-pay 30 | version: v1 31 | spec: 32 | replicas: 1 33 | selector: 34 | matchLabels: 35 | app: micro-pay 36 | version: v1 37 | template: 38 | metadata: 39 | labels: 40 | app: micro-pay 41 | version: v1 42 | spec: 43 | #设置的阿里云私有镜像仓库登陆信息的secret(对应2.1.2的设置) 44 | imagePullSecrets: 45 | - name: regcred 46 | containers: 47 | - name: micro-pay 48 | image: registry.cn-hangzhou.aliyuncs.com/wudimanong/micro-pay:1.0-SNAPSHOT 49 | imagePullPolicy: Always 50 | tty: true 51 | ports: 52 | - name: http 53 | protocol: TCP 54 | containerPort: 19092 55 | #指定服务gRPC端口 56 | - name: grpc 57 | protocol: TCP 58 | containerPort: 18888 -------------------------------------------------------------------------------- /k8s/virtual-service-all.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: networking.istio.io/v1alpha3 2 | kind: VirtualService 3 | metadata: 4 | name: micro-api-route 5 | spec: 6 | #用于定义流量被发送到的目标主机(这里为部署在k8s中的micro-api服务) 7 | hosts: 8 | - micro-api.default.svc.cluster.local 9 | #将VirtualService绑定到Istio网关,通过网关来暴露路由目标 10 | gateways: 11 | - micro-gateway 12 | http: 13 | - route: 14 | #设置旧版本(V1)版本的流量占比为70% 15 | - destination: 16 | host: micro-api.default.svc.cluster.local 17 | subset: v1 18 | #通过权重值来设置流量占比 19 | weight: 0 20 | #设置新版本(V2)版本的流量占比为30% 21 | - destination: 22 | host: micro-api.default.svc.cluster.local 23 | subset: v2 24 | weight: 100 -------------------------------------------------------------------------------- /micro-api/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8u191-jre-alpine3.9 2 | ENTRYPOINT ["/usr/bin/java", "-jar", "/app.jar"] 3 | ARG JAR_FILE 4 | ADD ${JAR_FILE} /app.jar 5 | EXPOSE 9090 -------------------------------------------------------------------------------- /micro-api/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.wudimanong 8 | micro-api 9 | 1.3-SNAPSHOT 10 | 11 | 12 | com.wudimanong 13 | istio-micro-demo 14 | 1.0-SNAPSHOT 15 | 16 | 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | 35 | 36 | org.projectlombok 37 | lombok 38 | 39 | 40 | 41 | com.alibaba 42 | fastjson 43 | 44 | 45 | 46 | com.wudimanong 47 | micro-order-client 48 | 1.0-SNAPSHOT 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-maven-plugin 58 | 59 | 60 | 61 | 62 | com.spotify 63 | dockerfile-maven-plugin 64 | 1.4.13 65 | 66 | 67 | javax.activation 68 | activation 69 | 1.1 70 | 71 | 72 | 73 | 74 | build-image 75 | package 76 | 77 | build 78 | push 79 | 80 | 81 | 82 | 83 | 84 | docker/Dockerfile 85 | ${docker.repository}/wudimanong/${project.artifactId} 86 | ${project.version} 87 | 88 | 89 | target/${project.build.finalName}.jar 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /micro-api/src/main/java/com/wudimanong/micro/api/MicroApi.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.api; 2 | 3 | import istio.fake.annotation.EnableFakeClients; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | /** 8 | * @author jiangqiao 9 | */ 10 | @SpringBootApplication 11 | @EnableFakeClients(basePackages = "com.wudimanong.micro.order.client") 12 | public class MicroApi { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(MicroApi.class, args); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /micro-api/src/main/java/com/wudimanong/micro/api/config/AutoResultReturnHandler.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.api.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.wudimanong.micro.api.exception.ResponseResult; 5 | import java.io.IOException; 6 | import java.io.PrintWriter; 7 | import javax.servlet.http.HttpServletResponse; 8 | import org.springframework.core.MethodParameter; 9 | import org.springframework.core.annotation.AnnotatedElementUtils; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | import org.springframework.web.context.request.NativeWebRequest; 12 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 13 | import org.springframework.web.method.support.ModelAndViewContainer; 14 | 15 | /** 16 | * @author jiangqiao 17 | */ 18 | public class AutoResultReturnHandler implements HandlerMethodReturnValueHandler { 19 | 20 | @Override 21 | public boolean supportsReturnType(MethodParameter returnType) { 22 | return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || 23 | returnType.hasMethodAnnotation(ResponseBody.class)); 24 | } 25 | 26 | /** 27 | * Controller层接口返回值统一封装方法 28 | * 29 | * @param returnValue 30 | * @param returnType 31 | * @param mavContainer 32 | * @param webRequest 33 | */ 34 | @Override 35 | public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, 36 | NativeWebRequest webRequest) { 37 | mavContainer.setRequestHandled(true); 38 | HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); 39 | response.setContentType("application/json;charset=UTF-8"); 40 | PrintWriter writer = null; 41 | try { 42 | writer = response.getWriter(); 43 | writer.print(JSON.toJSONString(ResponseResult.OK(returnValue))); 44 | writer.flush(); 45 | } catch (IOException e) { 46 | e.printStackTrace(); 47 | } finally { 48 | if (writer != null) { 49 | writer.close(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /micro-api/src/main/java/com/wudimanong/micro/api/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.api.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import org.springframework.beans.factory.InitializingBean; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 9 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; 10 | 11 | /** 12 | * @author jiangqiao 13 | */ 14 | @Configuration 15 | public class WebMvcConfig implements InitializingBean { 16 | 17 | @Autowired 18 | RequestMappingHandlerAdapter requestMappingHandlerAdapter; 19 | 20 | @Override 21 | public void afterPropertiesSet() { 22 | List returnValueHandlers = requestMappingHandlerAdapter 23 | .getReturnValueHandlers(); 24 | List list = new ArrayList<>(); 25 | //自定义returnHandler 26 | list.add(new AutoResultReturnHandler()); 27 | list.addAll(returnValueHandlers); 28 | requestMappingHandlerAdapter.setReturnValueHandlers(list); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /micro-api/src/main/java/com/wudimanong/micro/api/controller/ApiOrderController.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.api.controller; 2 | 3 | import com.wudimanong.micro.api.exception.ServiceException; 4 | import com.wudimanong.micro.order.client.OrderServiceClient; 5 | import com.wudimanong.micro.order.client.bo.CreateOrderBO; 6 | import com.wudimanong.micro.order.client.dto.CreateOrderDTO; 7 | import com.wudimanong.micro.order.client.dto.result.GlobalCodeEnum; 8 | import com.wudimanong.micro.order.client.dto.result.ResponseResult; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.PostMapping; 11 | import org.springframework.web.bind.annotation.RequestBody; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RestController; 14 | 15 | /** 16 | * @author jiangqiao 17 | */ 18 | @RestController 19 | @RequestMapping("/api/order") 20 | public class ApiOrderController { 21 | 22 | /** 23 | * 订单微服务api接口依赖 24 | */ 25 | @Autowired 26 | OrderServiceClient orderServiceClient; 27 | 28 | /** 29 | * 下单接口 30 | * 31 | * @param createOrderDTO 32 | * @return 33 | */ 34 | @PostMapping("/create") 35 | public CreateOrderBO create(@RequestBody CreateOrderDTO createOrderDTO) { 36 | ResponseResult result = orderServiceClient.create(createOrderDTO); 37 | if (result.getCode().equals(GlobalCodeEnum.GL_SUCC_0.getCode())) { 38 | return result.getData(); 39 | } else { 40 | throw new ServiceException(result.getCode(), result.getMessage()); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /micro-api/src/main/java/com/wudimanong/micro/api/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.api.controller; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.web.bind.annotation.GetMapping; 5 | import org.springframework.web.bind.annotation.RequestMapping; 6 | import org.springframework.web.bind.annotation.RestController; 7 | 8 | /** 9 | * @author jiangqiao 10 | */ 11 | @Slf4j 12 | @RestController 13 | @RequestMapping("/test") 14 | public class TestController { 15 | 16 | @GetMapping("/test") 17 | public String test() { 18 | log.info("测试接口被调用!"); 19 | return "V3|无依赖测试接口返回->OK!"; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /micro-api/src/main/java/com/wudimanong/micro/api/exception/GlobalCodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.api.exception; 2 | 3 | /** 4 | * @author qiaojiang 5 | */ 6 | public enum GlobalCodeEnum { 7 | 8 | /** 9 | * 全局返回码定义 - 0000开头 10 | */ 11 | GL_SUCC_0(0, "成功"), 12 | GL_FAIL_9995(9995, "依赖服务异常"), 13 | GL_FAIL_9996(9996, "不支持的HttpMethod"), 14 | GL_FAIL_9997(9997, "HTTP错误"), 15 | GL_FAIL_9998(9998, "参数错误"), 16 | GL_FAIL_9999(9999, "系统异常"); 17 | /** 18 | * 编码 19 | */ 20 | private Integer code; 21 | 22 | /** 23 | * 描述 24 | */ 25 | private String desc; 26 | 27 | 28 | GlobalCodeEnum(Integer code, String desc) { 29 | this.code = code; 30 | this.desc = desc; 31 | } 32 | 33 | /** 34 | * 根据编码获取枚举类型 35 | * 36 | * @param code 编码 37 | * @return 38 | */ 39 | public static GlobalCodeEnum getByCode(String code) { 40 | //判空 41 | if (code == null) { 42 | return null; 43 | } 44 | //循环处理 45 | GlobalCodeEnum[] values = GlobalCodeEnum.values(); 46 | for (GlobalCodeEnum value : values) { 47 | if (value.getCode().equals(code)) { 48 | return value; 49 | } 50 | } 51 | return null; 52 | } 53 | 54 | public Integer getCode() { 55 | return code; 56 | } 57 | 58 | public String getDesc() { 59 | return desc; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /micro-api/src/main/java/com/wudimanong/micro/api/exception/ResponseResult.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.api.exception; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 5 | import java.io.Serializable; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * @author qiaojiang 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @JsonPropertyOrder({"code", "message", "data"}) 19 | public class ResponseResult implements Serializable { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | /** 24 | * 返回的对象 25 | */ 26 | @JsonInclude(JsonInclude.Include.NON_NULL) 27 | private T data; 28 | /** 29 | * 返回的编码 30 | */ 31 | private Integer code; 32 | /** 33 | * 返回的信息 34 | */ 35 | private String message; 36 | 37 | /** 38 | * @return 响应结果 39 | */ 40 | public static ResponseResult OK() { 41 | return packageObject("", GlobalCodeEnum.GL_SUCC_0); 42 | } 43 | 44 | /** 45 | * @param data 返回的数据 46 | * @param 返回的数据类型 47 | * @return 响应结果 48 | */ 49 | public static ResponseResult OK(T data) { 50 | return packageObject(data, GlobalCodeEnum.GL_SUCC_0); 51 | } 52 | 53 | /** 54 | * 对返回的消息进行打包 55 | * 56 | * @param data 返回的数据 57 | * @param globalCodeEnum 自定义的返回码枚举类型 58 | * @param 返回的数据类型 59 | * @return 响应结果 60 | */ 61 | public static ResponseResult packageObject(T data, GlobalCodeEnum globalCodeEnum) { 62 | ResponseResult responseResult = new ResponseResult<>(); 63 | responseResult.setCode(globalCodeEnum.getCode()); 64 | responseResult.setMessage(globalCodeEnum.getDesc()); 65 | responseResult.setData(data); 66 | return responseResult; 67 | } 68 | 69 | /** 70 | * 对返回的消息进行打包 71 | * 72 | * @param data 返回的数据 73 | * @param code 返回的状态码 74 | * @param message 返回的消息 75 | * @param 返回的数据类型 76 | * @return 响应结果 77 | */ 78 | public static ResponseResult packageObject(T data, Integer code, String message) { 79 | ResponseResult responseResult = new ResponseResult<>(); 80 | responseResult.setCode(code); 81 | responseResult.setMessage(message); 82 | responseResult.setData(data); 83 | return responseResult; 84 | } 85 | 86 | /** 87 | * 系统服务不可用 88 | * 89 | * @param globalCodeEnum Feign依赖服务不可用的返回码枚举类型 90 | * @param 返回的数据类型 91 | * @return 响应结果 92 | */ 93 | public static ResponseResult systemError(GlobalCodeEnum globalCodeEnum) { 94 | return packageObject(null, globalCodeEnum); 95 | } 96 | 97 | /** 98 | * 未查询到相关的数据 99 | * 100 | * @param globalCodeEnum 未查询到相关信息的返回码枚举类型 101 | * @param 返回的数据类型 102 | * @return 响应结果 103 | */ 104 | public static ResponseResult noData(GlobalCodeEnum globalCodeEnum) { 105 | return packageObject(null, globalCodeEnum); 106 | } 107 | 108 | /** 109 | * 系统异常(使用默认的异常返回码) 110 | * 111 | * @param 返回的数据类型 112 | * @return 响应结果 113 | */ 114 | public static ResponseResult systemException() { 115 | return packageObject(null, GlobalCodeEnum.GL_FAIL_9999); 116 | } 117 | 118 | /** 119 | * 系统异常 120 | * 121 | * @param globalCodeEnum 异常返回码枚举类型 122 | * @param 返回的数据类型 123 | * @return 响应结果 124 | */ 125 | public static ResponseResult systemException(GlobalCodeEnum globalCodeEnum) { 126 | return packageObject(null, globalCodeEnum); 127 | } 128 | 129 | /** 130 | * 自定义系统异常信息 131 | * 132 | * @param code 133 | * @param message 自定义消息 134 | * @param 135 | * @return 136 | */ 137 | public static ResponseResult systemException(Integer code, String message) { 138 | return packageObject(null, code, message); 139 | } 140 | } -------------------------------------------------------------------------------- /micro-api/src/main/java/com/wudimanong/micro/api/exception/ServiceException.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.api.exception; 2 | 3 | /** 4 | * @author jiangqiao 5 | */ 6 | public class ServiceException extends RuntimeException { 7 | 8 | private final Integer code; 9 | 10 | public ServiceException(Integer code, String message) { 11 | super(message); 12 | this.code = code; 13 | } 14 | 15 | public ServiceException(Integer code, String message, Throwable e) { 16 | super(message, e); 17 | this.code = code; 18 | } 19 | 20 | public Integer getCode() { 21 | return code; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /micro-api/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | fake: 2 | micro-order: 127.0.0.1:9091 3 | -------------------------------------------------------------------------------- /micro-api/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: micro-api 4 | server: 5 | port: 9090 6 | -------------------------------------------------------------------------------- /micro-order-client/micro-order-client.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /micro-order-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | istio-micro-demo 7 | com.wudimanong 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.wudimanong 13 | micro-order-client 14 | 1.0-SNAPSHOT 15 | 16 | 17 | 18 | 19 | org.projectlombok 20 | lombok 21 | 22 | 23 | 24 | com.alibaba 25 | fastjson 26 | 27 | 28 | 29 | istio.fake 30 | istio-fake 31 | 1.0-SNAPSHOT 32 | 33 | 34 | -------------------------------------------------------------------------------- /micro-order-client/src/main/java/com/wudimanong/micro/order/client/OrderServiceClient.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.client; 2 | 3 | import com.wudimanong.micro.order.client.bo.CreateOrderBO; 4 | import com.wudimanong.micro.order.client.dto.CreateOrderDTO; 5 | import com.wudimanong.micro.order.client.dto.result.ResponseResult; 6 | import istio.fake.annotation.FakeClient; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | 11 | /** 12 | * @author jiangqiao 13 | */ 14 | @FakeClient(name = "micro-order") 15 | @RequestMapping("/order") 16 | public interface OrderServiceClient { 17 | 18 | /** 19 | * 订单创建 20 | * 21 | * @param createOrderDTO 22 | * @return 23 | */ 24 | @PostMapping("/create") 25 | ResponseResult create(@RequestBody CreateOrderDTO createOrderDTO); 26 | 27 | } 28 | -------------------------------------------------------------------------------- /micro-order-client/src/main/java/com/wudimanong/micro/order/client/bo/CreateOrderBO.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.client.bo; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Builder; 5 | import lombok.Data; 6 | import lombok.NoArgsConstructor; 7 | 8 | /** 9 | * @author jiangqiao 10 | */ 11 | @Data 12 | @Builder 13 | @AllArgsConstructor 14 | @NoArgsConstructor 15 | public class CreateOrderBO { 16 | 17 | /** 18 | * 订单号 19 | */ 20 | private String orderId; 21 | /** 22 | * 订单ID 23 | */ 24 | private Integer status; 25 | } 26 | -------------------------------------------------------------------------------- /micro-order-client/src/main/java/com/wudimanong/micro/order/client/dto/CreateOrderDTO.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.client.dto; 2 | 3 | import lombok.Data; 4 | 5 | /** 6 | * @author jiangqiao 7 | */ 8 | @Data 9 | public class CreateOrderDTO { 10 | 11 | /** 12 | * 业务订单号 13 | */ 14 | private String businessId; 15 | 16 | /** 17 | * 下单金额 18 | */ 19 | private Integer amount; 20 | 21 | /** 22 | * 下单渠道 23 | */ 24 | private Integer channel; 25 | 26 | } 27 | -------------------------------------------------------------------------------- /micro-order-client/src/main/java/com/wudimanong/micro/order/client/dto/result/GlobalCodeEnum.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.client.dto.result; 2 | 3 | /** 4 | * @author qiaojiang 5 | */ 6 | public enum GlobalCodeEnum { 7 | 8 | /** 9 | * 全局返回码定义 - 0000开头 10 | */ 11 | GL_SUCC_0(0, "成功"), 12 | GL_FAIL_9995(9995, "依赖服务异常"), 13 | GL_FAIL_9996(9996, "不支持的HttpMethod"), 14 | GL_FAIL_9997(9997, "HTTP错误"), 15 | GL_FAIL_9998(9998, "参数错误"), 16 | GL_FAIL_9999(9999, "系统异常"); 17 | /** 18 | * 编码 19 | */ 20 | private Integer code; 21 | 22 | /** 23 | * 描述 24 | */ 25 | private String desc; 26 | 27 | 28 | GlobalCodeEnum(Integer code, String desc) { 29 | this.code = code; 30 | this.desc = desc; 31 | } 32 | 33 | /** 34 | * 根据编码获取枚举类型 35 | * 36 | * @param code 编码 37 | * @return 38 | */ 39 | public static GlobalCodeEnum getByCode(String code) { 40 | //判空 41 | if (code == null) { 42 | return null; 43 | } 44 | //循环处理 45 | GlobalCodeEnum[] values = GlobalCodeEnum.values(); 46 | for (GlobalCodeEnum value : values) { 47 | if (value.getCode().equals(code)) { 48 | return value; 49 | } 50 | } 51 | return null; 52 | } 53 | 54 | public Integer getCode() { 55 | return code; 56 | } 57 | 58 | public String getDesc() { 59 | return desc; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /micro-order-client/src/main/java/com/wudimanong/micro/order/client/dto/result/ResponseResult.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.client.dto.result; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import com.fasterxml.jackson.annotation.JsonPropertyOrder; 5 | import java.io.Serializable; 6 | import lombok.AllArgsConstructor; 7 | import lombok.Builder; 8 | import lombok.Data; 9 | import lombok.NoArgsConstructor; 10 | 11 | /** 12 | * @author qiaojiang 13 | */ 14 | @Data 15 | @Builder 16 | @NoArgsConstructor 17 | @AllArgsConstructor 18 | @JsonPropertyOrder({"code", "message", "data"}) 19 | public class ResponseResult implements Serializable { 20 | 21 | private static final long serialVersionUID = 1L; 22 | 23 | /** 24 | * 返回的对象 25 | */ 26 | @JsonInclude(JsonInclude.Include.NON_NULL) 27 | private T data; 28 | /** 29 | * 返回的编码 30 | */ 31 | private Integer code; 32 | /** 33 | * 返回的信息 34 | */ 35 | private String message; 36 | 37 | /** 38 | * @return 响应结果 39 | */ 40 | public static ResponseResult OK() { 41 | return packageObject("", GlobalCodeEnum.GL_SUCC_0); 42 | } 43 | 44 | /** 45 | * @param data 返回的数据 46 | * @param 返回的数据类型 47 | * @return 响应结果 48 | */ 49 | public static ResponseResult OK(T data) { 50 | return packageObject(data, GlobalCodeEnum.GL_SUCC_0); 51 | } 52 | 53 | /** 54 | * 对返回的消息进行打包 55 | * 56 | * @param data 返回的数据 57 | * @param globalCodeEnum 自定义的返回码枚举类型 58 | * @param 返回的数据类型 59 | * @return 响应结果 60 | */ 61 | public static ResponseResult packageObject(T data, GlobalCodeEnum globalCodeEnum) { 62 | ResponseResult responseResult = new ResponseResult<>(); 63 | responseResult.setCode(globalCodeEnum.getCode()); 64 | responseResult.setMessage(globalCodeEnum.getDesc()); 65 | responseResult.setData(data); 66 | return responseResult; 67 | } 68 | 69 | /** 70 | * 对返回的消息进行打包 71 | * 72 | * @param data 返回的数据 73 | * @param code 返回的状态码 74 | * @param message 返回的消息 75 | * @param 返回的数据类型 76 | * @return 响应结果 77 | */ 78 | public static ResponseResult packageObject(T data, Integer code, String message) { 79 | ResponseResult responseResult = new ResponseResult<>(); 80 | responseResult.setCode(code); 81 | responseResult.setMessage(message); 82 | responseResult.setData(data); 83 | return responseResult; 84 | } 85 | 86 | /** 87 | * 系统服务不可用 88 | * 89 | * @param globalCodeEnum Feign依赖服务不可用的返回码枚举类型 90 | * @param 返回的数据类型 91 | * @return 响应结果 92 | */ 93 | public static ResponseResult systemError(GlobalCodeEnum globalCodeEnum) { 94 | return packageObject(null, globalCodeEnum); 95 | } 96 | 97 | /** 98 | * 未查询到相关的数据 99 | * 100 | * @param globalCodeEnum 未查询到相关信息的返回码枚举类型 101 | * @param 返回的数据类型 102 | * @return 响应结果 103 | */ 104 | public static ResponseResult noData(GlobalCodeEnum globalCodeEnum) { 105 | return packageObject(null, globalCodeEnum); 106 | } 107 | 108 | /** 109 | * 系统异常(使用默认的异常返回码) 110 | * 111 | * @param 返回的数据类型 112 | * @return 响应结果 113 | */ 114 | public static ResponseResult systemException() { 115 | return packageObject(null, GlobalCodeEnum.GL_FAIL_9999); 116 | } 117 | 118 | /** 119 | * 系统异常 120 | * 121 | * @param globalCodeEnum 异常返回码枚举类型 122 | * @param 返回的数据类型 123 | * @return 响应结果 124 | */ 125 | public static ResponseResult systemException(GlobalCodeEnum globalCodeEnum) { 126 | return packageObject(null, globalCodeEnum); 127 | } 128 | 129 | /** 130 | * 自定义系统异常信息 131 | * 132 | * @param code 133 | * @param message 自定义消息 134 | * @param 135 | * @return 136 | */ 137 | public static ResponseResult systemException(Integer code, String message) { 138 | return packageObject(null, code, message); 139 | } 140 | } -------------------------------------------------------------------------------- /micro-order/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8u191-jre-alpine3.9 2 | ENTRYPOINT ["/usr/bin/java", "-jar", "/app.jar"] 3 | ARG JAR_FILE 4 | ADD ${JAR_FILE} /app.jar 5 | EXPOSE 9091 -------------------------------------------------------------------------------- /micro-order/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.wudimanong 8 | micro-order 9 | 1.0-SNAPSHOT 10 | 11 | 12 | com.wudimanong 13 | istio-micro-demo 14 | 1.0-SNAPSHOT 15 | 16 | 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | 35 | com.wudimanong 36 | micro-order-client 37 | 1.0-SNAPSHOT 38 | 39 | 40 | 41 | 42 | com.wudimanong 43 | micro-pay-client 44 | 1.0-SNAPSHOT 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | org.springframework.boot 53 | spring-boot-maven-plugin 54 | 55 | 56 | 57 | com.spotify 58 | dockerfile-maven-plugin 59 | 1.4.13 60 | 61 | 62 | javax.activation 63 | activation 64 | 1.1 65 | 66 | 67 | 68 | 69 | build-image 70 | package 71 | 72 | build 73 | push 74 | 75 | 76 | 77 | 78 | 79 | docker/Dockerfile 80 | ${docker.repository}/wudimanong/${project.artifactId} 81 | ${project.version} 82 | 83 | 84 | target/${project.build.finalName}.jar 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/MicroOrder.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author jiangqiao 8 | */ 9 | @SpringBootApplication 10 | public class MicroOrder { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(MicroOrder.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/config/AutoResultReturnHandler.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.config; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.wudimanong.micro.order.client.dto.result.ResponseResult; 5 | import java.io.IOException; 6 | import java.io.PrintWriter; 7 | import javax.servlet.http.HttpServletResponse; 8 | import org.springframework.core.MethodParameter; 9 | import org.springframework.core.annotation.AnnotatedElementUtils; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | import org.springframework.web.context.request.NativeWebRequest; 12 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 13 | import org.springframework.web.method.support.ModelAndViewContainer; 14 | 15 | /** 16 | * @author jiangqiao 17 | */ 18 | public class AutoResultReturnHandler implements HandlerMethodReturnValueHandler { 19 | 20 | @Override 21 | public boolean supportsReturnType(MethodParameter returnType) { 22 | return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) || 23 | returnType.hasMethodAnnotation(ResponseBody.class)); 24 | } 25 | 26 | /** 27 | * Controller层接口返回值统一封装方法 28 | * 29 | * @param returnValue 30 | * @param returnType 31 | * @param mavContainer 32 | * @param webRequest 33 | */ 34 | @Override 35 | public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, 36 | NativeWebRequest webRequest) { 37 | mavContainer.setRequestHandled(true); 38 | HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class); 39 | response.setContentType("application/json;charset=UTF-8"); 40 | PrintWriter writer = null; 41 | try { 42 | writer = response.getWriter(); 43 | writer.print(JSON.toJSONString(ResponseResult.OK(returnValue))); 44 | writer.flush(); 45 | } catch (IOException e) { 46 | e.printStackTrace(); 47 | } finally { 48 | if (writer != null) { 49 | writer.close(); 50 | } 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/config/GrpcClientCommandLineRunner.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.config; 2 | 3 | import lombok.extern.slf4j.Slf4j; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.CommandLineRunner; 6 | import org.springframework.stereotype.Component; 7 | 8 | /** 9 | * @author jiangqiao 10 | */ 11 | @Component 12 | @Slf4j 13 | public class GrpcClientCommandLineRunner implements CommandLineRunner { 14 | 15 | @Autowired 16 | GrpcClientConfiguration configuration; 17 | 18 | @Override 19 | public void run(String... args) throws Exception { 20 | //开启gRPC客户端 21 | configuration.start(); 22 | 23 | //添加客户端关闭的逻辑 24 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 25 | try { 26 | configuration.shutdown(); 27 | } catch (InterruptedException e) { 28 | e.printStackTrace(); 29 | } 30 | })); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/config/GrpcClientConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.config; 2 | 3 | import com.wudimanong.micro.pay.proto.PayServiceGrpc; 4 | import io.grpc.ManagedChannel; 5 | import io.grpc.ManagedChannelBuilder; 6 | import java.util.concurrent.TimeUnit; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.stereotype.Component; 10 | 11 | /** 12 | * @author jiangqiao 13 | */ 14 | @Slf4j 15 | @Component 16 | public class GrpcClientConfiguration { 17 | 18 | /** 19 | * 支付gRPC Server的地址 20 | */ 21 | @Value("${server-host}") 22 | private String host; 23 | 24 | /** 25 | * 支付gRPC Server的端口 26 | */ 27 | @Value("${server-port}") 28 | private int port; 29 | 30 | private ManagedChannel channel; 31 | 32 | /** 33 | * 支付服务stub对象 34 | */ 35 | private PayServiceGrpc.PayServiceBlockingStub stub; 36 | 37 | public void start() { 38 | //开启channel 39 | channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext().build(); 40 | 41 | //通过channel获取到服务端的stub 42 | stub = PayServiceGrpc.newBlockingStub(channel); 43 | log.info("gRPC client started, server address: {}:{}", host, port); 44 | } 45 | 46 | public void shutdown() throws InterruptedException { 47 | //调用shutdown方法后等待1秒关闭channel 48 | channel.shutdown().awaitTermination(1, TimeUnit.SECONDS); 49 | log.info("gRPC client shut down successfully."); 50 | } 51 | 52 | public PayServiceGrpc.PayServiceBlockingStub getStub() { 53 | return this.stub; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import org.springframework.beans.factory.InitializingBean; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.web.method.support.HandlerMethodReturnValueHandler; 9 | import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; 10 | 11 | /** 12 | * @author jiangqiao 13 | */ 14 | @Configuration 15 | public class WebMvcConfig implements InitializingBean { 16 | 17 | @Autowired 18 | RequestMappingHandlerAdapter requestMappingHandlerAdapter; 19 | 20 | @Override 21 | public void afterPropertiesSet() { 22 | List returnValueHandlers = requestMappingHandlerAdapter 23 | .getReturnValueHandlers(); 24 | List list = new ArrayList<>(); 25 | //自定义returnHandler 26 | list.add(new AutoResultReturnHandler()); 27 | list.addAll(returnValueHandlers); 28 | requestMappingHandlerAdapter.setReturnValueHandlers(list); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/controller/OrderController.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.controller; 2 | 3 | import com.wudimanong.micro.order.client.bo.CreateOrderBO; 4 | import com.wudimanong.micro.order.client.dto.CreateOrderDTO; 5 | import com.wudimanong.micro.order.service.OrderService; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.web.bind.annotation.PostMapping; 8 | import org.springframework.web.bind.annotation.RequestBody; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | /** 13 | * @author jiangqiao 14 | */ 15 | @RestController 16 | @RequestMapping("/order") 17 | public class OrderController { 18 | 19 | @Autowired 20 | OrderService orderServiceImpl; 21 | 22 | /** 23 | * 下单接口 24 | * 25 | * @param createOrderDTO 26 | * @return 27 | */ 28 | @PostMapping("/create") 29 | public CreateOrderBO create(@RequestBody CreateOrderDTO createOrderDTO) { 30 | return orderServiceImpl.create(createOrderDTO); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/exception/ServiceException.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.exception; 2 | 3 | /** 4 | * @author jiangqiao 5 | */ 6 | public class ServiceException extends RuntimeException { 7 | 8 | private final Integer code; 9 | 10 | public ServiceException(Integer code, String message) { 11 | super(message); 12 | this.code = code; 13 | } 14 | 15 | public ServiceException(Integer code, String message, Throwable e) { 16 | super(message, e); 17 | this.code = code; 18 | } 19 | 20 | public Integer getCode() { 21 | return code; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/service/OrderService.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.service; 2 | 3 | 4 | import com.wudimanong.micro.order.client.bo.CreateOrderBO; 5 | import com.wudimanong.micro.order.client.dto.CreateOrderDTO; 6 | 7 | /** 8 | * @author jiangqiao 9 | */ 10 | public interface OrderService { 11 | 12 | /** 13 | * 下单接口 14 | * 15 | * @param createOrderDTO 16 | * @return 17 | */ 18 | CreateOrderBO create(CreateOrderDTO createOrderDTO); 19 | } 20 | -------------------------------------------------------------------------------- /micro-order/src/main/java/com/wudimanong/micro/order/service/impl/OrderServiceImpl.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.order.service.impl; 2 | 3 | import com.wudimanong.micro.order.client.bo.CreateOrderBO; 4 | import com.wudimanong.micro.order.client.dto.CreateOrderDTO; 5 | import com.wudimanong.micro.order.config.GrpcClientConfiguration; 6 | import com.wudimanong.micro.order.service.OrderService; 7 | import com.wudimanong.micro.pay.proto.PayRequest; 8 | import com.wudimanong.micro.pay.proto.PayResponse; 9 | import java.util.Random; 10 | import lombok.extern.slf4j.Slf4j; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Service; 13 | 14 | /** 15 | * @author jiangqiao 16 | */ 17 | @Slf4j 18 | @Service 19 | public class OrderServiceImpl implements OrderService { 20 | 21 | /** 22 | * 引入gRPC客户端配置依赖 23 | */ 24 | @Autowired 25 | GrpcClientConfiguration gRpcClent; 26 | 27 | @Override 28 | public CreateOrderBO create(CreateOrderDTO createOrderDTO) { 29 | log.info("现在开始处理下单请求....."); 30 | //生成订单号 31 | String orderId = String.valueOf(new Random(100).nextInt(100000) + System.currentTimeMillis()); 32 | //构建支付请求(gRPC调用) 33 | PayRequest payRequest = PayRequest.newBuilder().setOrderId(orderId).setAmount(createOrderDTO.getAmount()) 34 | .build(); 35 | //使用stub发送请求到服务端 36 | PayResponse payResponse = gRpcClent.getStub().doPay(payRequest); 37 | log.info("pay gRpc response->" + payResponse.toString()); 38 | return CreateOrderBO.builder().orderId(orderId).status(payResponse.getStatus()).build(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /micro-order/src/main/resources/application-dev.yml: -------------------------------------------------------------------------------- 1 | #支付微服务Grpc服务地址、端口配置 2 | server-host: 127.0.0.1 3 | server-port: 18888 4 | -------------------------------------------------------------------------------- /micro-order/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: micro-order 4 | server: 5 | port: 9091 6 | 7 | #支付微服务Grpc服务地址、端口配置 8 | server-host: ${grpc_server_host} 9 | server-port: ${grpc_server_port} 10 | -------------------------------------------------------------------------------- /micro-pay-client/micro-pay-client.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /micro-pay-client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | istio-micro-demo 7 | com.wudimanong 8 | 1.0-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | com.wudimanong 13 | micro-pay-client 14 | 1.0-SNAPSHOT 15 | 16 | 17 | 18 | 19 | org.projectlombok 20 | lombok 21 | 22 | 23 | 24 | com.alibaba 25 | fastjson 26 | 27 | 28 | 29 | io.grpc 30 | grpc-all 31 | 1.36.1 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | kr.motd.maven 40 | os-maven-plugin 41 | 1.6.2 42 | 43 | 44 | 45 | 46 | org.xolstice.maven.plugins 47 | protobuf-maven-plugin 48 | 0.6.1 49 | 50 | com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier} 51 | grpc-java 52 | io.grpc:protoc-gen-grpc-java:1.36.0:exe:${os.detected.classifier} 53 | 54 | 55 | 56 | 57 | compile 58 | compile-custom 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /micro-pay-client/src/main/proto/paycore.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package com.wudimanong.pay.client; 4 | 5 | option java_multiple_files = true; 6 | option java_package = "com.wudimanong.micro.pay.proto"; 7 | 8 | service PayService { 9 | //定义支付rpc方法 10 | rpc doPay (PayRequest) returns (PayResponse); 11 | } 12 | 13 | message PayRequest { 14 | string orderId = 1; 15 | int32 amount=2; 16 | } 17 | 18 | message PayResponse { 19 | int32 status = 1; 20 | } -------------------------------------------------------------------------------- /micro-pay/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openjdk:8u191-jre-alpine3.9 2 | ENTRYPOINT ["/usr/bin/java", "-jar", "/app.jar"] 3 | ARG JAR_FILE 4 | ADD ${JAR_FILE} /app.jar 5 | EXPOSE 9092 -------------------------------------------------------------------------------- /micro-pay/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.wudimanong 8 | micro-pay 9 | 1.0-SNAPSHOT 10 | 11 | 12 | com.wudimanong 13 | istio-micro-demo 14 | 1.0-SNAPSHOT 15 | 16 | 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter 22 | 23 | 24 | 25 | org.springframework.boot 26 | spring-boot-starter-web 27 | 28 | 29 | org.springframework.boot 30 | spring-boot-starter-test 31 | test 32 | 33 | 34 | 35 | org.projectlombok 36 | lombok 37 | 38 | 39 | 40 | 41 | com.wudimanong 42 | micro-pay-client 43 | 1.0-SNAPSHOT 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | org.springframework.boot 52 | spring-boot-maven-plugin 53 | 54 | 55 | 56 | com.spotify 57 | dockerfile-maven-plugin 58 | 1.4.13 59 | 60 | 61 | javax.activation 62 | activation 63 | 1.1 64 | 65 | 66 | 67 | 68 | build-image 69 | package 70 | 71 | build 72 | push 73 | 74 | 75 | 76 | 77 | 78 | docker/Dockerfile 79 | ${docker.repository}/wudimanong/${project.artifactId} 80 | ${project.version} 81 | 82 | 83 | target/${project.build.finalName}.jar 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /micro-pay/src/main/java/com/wudimanong/micro/pay/MicroPay.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.pay; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | /** 7 | * @author jiangqiao 8 | */ 9 | @SpringBootApplication 10 | public class MicroPay { 11 | 12 | public static void main(String[] args) { 13 | SpringApplication.run(MicroPay.class, args); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /micro-pay/src/main/java/com/wudimanong/micro/pay/config/GrpcCommandLineRunner.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.pay.config; 2 | 3 | import org.springframework.beans.factory.annotation.Autowired; 4 | import org.springframework.boot.CommandLineRunner; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * @author jiangqiao 9 | */ 10 | @Component 11 | public class GrpcCommandLineRunner implements CommandLineRunner { 12 | 13 | @Autowired 14 | GrpcServerConfiguration configuration; 15 | 16 | @Override 17 | public void run(String... args) throws Exception { 18 | configuration.start(); 19 | configuration.block(); 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /micro-pay/src/main/java/com/wudimanong/micro/pay/config/GrpcServerConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.pay.config; 2 | 3 | import com.wudimanong.micro.pay.provider.PayCoreProvider; 4 | import io.grpc.Server; 5 | import io.grpc.ServerBuilder; 6 | import java.io.IOException; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.stereotype.Component; 11 | 12 | /** 13 | * @author jiangqiao 14 | */ 15 | @Slf4j 16 | @Component 17 | public class GrpcServerConfiguration { 18 | 19 | @Autowired 20 | PayCoreProvider service; 21 | 22 | /** 23 | * 注入配置文件中的端口信息 24 | */ 25 | @Value("${grpc.server-port}") 26 | private int port; 27 | private Server server; 28 | 29 | public void start() throws IOException { 30 | // 构建服务端 31 | log.info("Starting gRPC on port {}.", port); 32 | server = ServerBuilder.forPort(port).addService(service).build().start(); 33 | log.info("gRPC server started, listening on {}.", port); 34 | 35 | // 添加服务端关闭的逻辑 36 | Runtime.getRuntime().addShutdownHook(new Thread(() -> { 37 | log.info("Shutting down gRPC server."); 38 | GrpcServerConfiguration.this.stop(); 39 | log.info("gRPC server shut down successfully."); 40 | })); 41 | } 42 | 43 | private void stop() { 44 | if (server != null) { 45 | // 关闭服务端 46 | server.shutdown(); 47 | } 48 | } 49 | 50 | public void block() throws InterruptedException { 51 | if (server != null) { 52 | // 服务端启动后直到应用关闭都处于阻塞状态,方便接收请求 53 | server.awaitTermination(); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /micro-pay/src/main/java/com/wudimanong/micro/pay/provider/PayCoreProvider.java: -------------------------------------------------------------------------------- 1 | package com.wudimanong.micro.pay.provider; 2 | 3 | import com.wudimanong.micro.pay.proto.PayRequest; 4 | import com.wudimanong.micro.pay.proto.PayResponse; 5 | import com.wudimanong.micro.pay.proto.PayServiceGrpc; 6 | import io.grpc.stub.StreamObserver; 7 | import lombok.extern.slf4j.Slf4j; 8 | import org.springframework.stereotype.Component; 9 | 10 | /** 11 | * @author jiangqiao 12 | */ 13 | @Slf4j 14 | @Component 15 | public class PayCoreProvider extends PayServiceGrpc.PayServiceImplBase { 16 | 17 | /** 18 | * 实现ProtoBuf中定义的服务方法 19 | * 20 | * @param request 21 | * @param responseStreamObserver 22 | */ 23 | @Override 24 | public void doPay(PayRequest request, StreamObserver responseStreamObserver) { 25 | //逻辑处理(简单模拟打印日志) 26 | log.info("处理gRPC支付处理请求,orderId->{};payAmount{}", request.getOrderId(), request.getAmount()); 27 | //构建返回对象(构建处理状态) 28 | PayResponse response = PayResponse.newBuilder().setStatus(2).build(); 29 | //设置数据响应 30 | responseStreamObserver.onNext(response); 31 | responseStreamObserver.onCompleted(); 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /micro-pay/src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | spring: 2 | application: 3 | name: micro-pay 4 | server: 5 | port: 9092 6 | 7 | #定义gRPC服务开放的端口 8 | grpc: 9 | server-port: 18888 10 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | com.wudimanong 7 | istio-micro-demo 8 | pom 9 | 1.0-SNAPSHOT 10 | 11 | 12 | org.springframework.boot 13 | spring-boot-starter-parent 14 | 2.1.5.RELEASE 15 | 16 | 17 | 18 | micro-api 19 | micro-order 20 | micro-pay 21 | micro-order-client 22 | micro-pay-client 23 | feign-istio-fake 24 | 25 | 26 | 27 | 28 | registry.cn-hangzhou.aliyuncs.com 29 | 30 | 31 | 32 | 33 | 34 | 35 | com.alibaba 36 | fastjson 37 | 1.2.70 38 | compile 39 | 40 | 41 | 42 | --------------------------------------------------------------------------------